द टैक्स हिस्टोरियन


9

परिचय

एक टैक्स कलेक्टर है जिसे अपने राज्य के करों का प्रबंधन करने में कुछ परेशानी है: ऐतिहासिक रिकॉर्ड एक महान आग में जल गए हैं।

वह यह पता लगाना चाहता है कि वर्तमान धन जहां से विरासत में मिला था, उसके संदर्भ में कितने संभावित अतीत हो सकते हैं। सौभाग्य से, उसका राज्य बहुत सरल है।

राज्य को 2 डी बुलियन मैट्रिक्स द्वारा मॉडल किया जा सकता है, जहां lकिसी को विरासत में मिला पैसा है, और Oजो नहीं है उसका प्रतिनिधित्व करता है। उदाहरण के लिए:

l O l l

O O O l

l O l O

O O O l

(यह हमेशा एक आयत होगी)

अगली पीढ़ी में, राज्य छोटा है (भेड़िये मजबूत हैं!)।

अगली पीढ़ी इस तरह दिखेगी, पिछली पीढ़ी पर आधारित ( xअगली पीढ़ी में एक वंशज के लिए एक प्लेसहोल्डर)

l O l l
 x x x
O O O l
 x x x
l O l O
 x x x
O O O l

एक वंशज पूर्वजों कि सीधे उनके आसपास हैं देखेंगे (तो ऊपर-बाईं ओर xदेखेंगे { l, O, O, O} को बुलाया असंरेखित आयताकार पड़ोस )

यदि केवल एक पूर्वज को विरासत में पैसा मिला है, तो वंशज उनसे धन प्राप्त करेगा। यदि एक से अधिक पूर्वजों को विरासत में पैसा मिला है, तो वे भाग लेंगे और वंशज विरासत में नहीं पैसा खत्म कर देगा। अगर किसी को पैसा विरासत में नहीं मिला है, तो वंशज को कोई पैसा विरासत में नहीं मिलेगा।

(एक से अधिक वंशज एक पूर्वज से विरासत में प्राप्त कर सकते हैं)

तो, अगली पीढ़ी की तरह दिखेगा:

​
 l l O

 l l O

 l l O
​

चुनौती

इनपुट

पीढ़ी की वर्तमान स्थिति, किन्हीं दो अलग-अलग मूल्यों के सरणियों के एक सरणी के रूप में, जहां आंतरिक सरणियां सभी समान लंबाई की हैं।

उदाहरण के लिए, ऊपर, यह हो सकता है:

[
  [True, True, False],
  [True, True, False],
  [True, True, False]
]

उत्पादन

एक पूर्णांक पिछली अद्वितीय पीढ़ियों की संख्या का प्रतिनिधित्व करता है जहां अगली पीढ़ी इनपुट है।

आप मान सकते हैं कि उत्तर हमेशा 2 ^ 30 से कम होगा - 1. (या 1073741823)।

पिछली पीढ़ी को एक "प्रीइमेज" कहा जाएगा और यह चुनौती प्रीमैसेज को गिनने की होगी ।

स्कोरिंग

यह है एक चुनौती, इसलिए प्रत्येक जमा को मेरे कंप्यूटर पर परीक्षण किया जाएगा, और प्रस्तुत करने वाले को कम से कम समय लगेगा।

उदाहरण इनपुट और आउटपुट

(जहाँ 1एक वंशज है जिसे विरासत में पैसा मिला है, और 0एक ऐसा वंशज है जिसे विरासत में पैसा नहीं मिला है)

इनपुट:

[[1, 0, 1],
 [0, 1, 0],
 [1, 0, 1]]

आउटपुट:

4

इनपुट:

[[1, 0, 1, 0, 0, 1, 1, 1],
 [1, 0, 1, 0, 0, 0, 1, 0],
 [1, 1, 1, 0, 0, 0, 1, 0],
 [1, 0, 1, 0, 0, 0, 1, 0],
 [1, 0, 1, 0, 0, 1, 1, 1]]

आउटपुट:

254

इनपुट:

[[1, 1, 0, 1, 0, 1, 0, 1, 1, 0],
 [1, 1, 0, 0, 0, 0, 1, 1, 1, 0],
 [1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
 [0, 1, 0, 0, 0, 0, 1, 1, 0, 0]]

आउटपुट:

11567

6
"lOOlLOOOOLLlololoLOLOLOOLOLOLL" वह सब है जब मैंने पहली बार पृष्ठ खोला था।
मैजिक ऑक्टोपस Urn

जवाबों:


4

BuDDy लाइब्रेरी का उपयोग करके C ++

यह द्विआधारी निर्णय आरेख के साथ खेलने के लिए एक अच्छा बहाना लग रहा था । राज्य को एक बड़े बूलियन फार्मूले में बदल दिया जाता है, जहां हमें उन तरीकों की संख्या गिननी होती है, जिसमें वह संतुष्ट हो सकते हैं। यह (कभी-कभी) लगता है की तुलना में अधिक कुशलता से किया जा सकता है।

राज्य को एक कार्यक्रम के रूप में दिया जाना चाहिए जो एक सपाट सरणी के रूप में स्थिर हो और स्पष्ट रूप से दिए गए आयाम हो। (अच्छा इनपुट पाठक के लिए एक निष्पादन के रूप में छोड़ दिया जाता है :-)

यहाँ शर्मनाक सरल कोड है:

#include <iostream>
#include <bdd.h>

// describe the kingdom here:

constexpr int ROWS = 4;
constexpr int COLS = 10;

constexpr int a[] = {
   1, 1, 0, 1, 0, 1, 0, 1, 1, 0,
   1, 1, 0, 0, 0, 0, 1, 1, 1, 0,
   1, 1, 0, 0, 0, 0, 0, 0, 0, 1,
   0, 1, 0, 0, 0, 0, 1, 1, 0, 0,
};

// end of description

// check dimensions
static_assert(ROWS*COLS*sizeof(int)==sizeof(a),
          "ROWS*COLS must be the number of entries of a");

// dimensions of previous generation
constexpr int R1 = ROWS+1;
constexpr int C1 = COLS+1;

// condition that exactly one is true
bdd one(bdd a, bdd b, bdd c, bdd d){
  bdd q = a & !b & !c & !d;
  q |= !a & b & !c & !d;
  q |= !a & !b & c & !d;
  q |= !a & !b & !c & d;
  return q;
}

int main()
{
  bdd_init(1000000, 10000); // tuneable, but not too important
  bdd_setvarnum(R1*C1);
  bdd q { bddtrue };
  for(int j=COLS-1; j>=0; j--) // handle high vars first
    for (int i=ROWS-1; i>=0; i--){
      int x=i+R1*j;
      bdd p=one(bdd_ithvar(x), bdd_ithvar(x+1),
                bdd_ithvar(x+R1), bdd_ithvar(x+R1+1));
      if (!a[COLS*i+j])
        p = !p;
      q &= p;
    }
  std::cout << "There are " << bdd_satcount(q) << " preimages\n";
  bdd_done();
}

डेबियन 8 (जेसी) के साथ संकलन करने के लिए, स्थापित करें libbdd-devऔर करें g++ -std=c++11 -o hist hist.cpp -lbdd। (ऑप्टिमाइज़िंग विकल्पों में लगभग कोई अंतर नहीं है क्योंकि वास्तविक कार्य लाइब्रेरी में किया जाता है।)

बड़े उदाहरण कचरा संग्रहण के बारे में संदेश दे सकते हैं। उन्हें दबाया जा सकता था, लेकिन मैं उन्हें देखना पसंद करता हूं।

bdd_satcountएक रिटर्न देता है double, लेकिन परिणाम की अपेक्षित सीमा के लिए यह काफी अच्छा है। एक ही गिनती तकनीक सटीक (बड़े) पूर्णांकों के साथ संभव है।

कोड के लिए अनुकूलित है ROWS<COLS। यदि आपके पास स्तंभों की तुलना में बहुत अधिक पंक्तियाँ हैं, तो मैट्रिक्स को स्थानांतरित करना एक अच्छा विचार हो सकता है।


2.39 सेकंड। यह आधा समय है जो मेरे पास था! इसे स्वीकार करते हुए चिह्नित करना।
२१:१२ बजे

1
@Artyer: क्या आप अपने सबसे लंबे समय तक चलने वाले छिपे हुए परीक्षण मामले को पोस्ट करना चाहेंगे? यदि आप चाहें तो आपके समाधान के साथ-साथ।
एंड्रयू एपस्टीन

@AndrewEpstein मैंने हाल ही में एक हार्डड्राइव विफलता की है और दोनों कोड और मूल परीक्षण मामलों को खो दिया है (उनमें से सैकड़ों थे, और वे अधिकतम 300-चौड़ा, 10-उच्च मुझे लगता है)। माफ़ करना।
अरेटियर

3

पायथन 2.7

यह सिर्फ एक भोला पहला प्रयास है। यह विशेष रूप से तेज़ नहीं है, लेकिन यह सही है।

पहला अवलोकन यह है कि प्रत्येक कोशिका पिछली पीढ़ी में ठीक चार कोशिकाओं पर निर्भर है। हम उन चार कोशिकाओं को 4-बिट संख्या (0-15) के रूप में दर्शा सकते हैं। नियमों के अनुसार, यदि पिछली पीढ़ी में ठीक एक पड़ोसी सेल है 1, तो वर्तमान पीढ़ी में एक दिया गया सेल होगा 1, अन्यथा, यह होगा 0। वे दो की शक्तियों के अनुरूप हैं, अर्थात् [1, 2, 4, 8]। जब चार पूर्वजों को 4-बिट संख्या के रूप में दर्शाया जाता है, तो किसी भी अन्य संख्या का परिणाम 0वर्तमान पीढ़ी में होगा। इस जानकारी के साथ, वर्तमान पीढ़ी में एक सेल को देखने पर, हम पिछली पीढ़ी में पड़ोस की संभावनाओं को क्रमशः चार में से एक या चार संभावनाओं में से एक तक सीमित कर सकते हैं।

मैंने पड़ोस का प्रतिनिधित्व करने के लिए चुना है:

32
10

जहां 0 सबसे कम महत्वपूर्ण बिट है, और इसी तरह।

दूसरा अवलोकन यह है कि वर्तमान पीढ़ी में दो आसन्न कोशिकाओं के लिए, पिछली पीढ़ी के दो पड़ोस ओवरलैप होते हैं:

32  32
10  10

या:

32
10

32
10

क्षैतिज मामले में, 2बाएं पड़ोस 3से दाएं पड़ोस से ओवरलैप होता है, और इसी तरह 0बाईं ओर और 1दाईं ओर। ऊर्ध्वाधर मामले में, 1शीर्ष पड़ोस 3से नीचे पड़ोस से ओवरलैप होता है, और इसी तरह 0शीर्ष 2पर और नीचे पर।

इस ओवरलैप का मतलब है कि हम पहले से ही चुने गए लोगों के आधार पर अभी तक अनजाने पड़ोस के लिए संभावनाओं को कम कर सकते हैं। कोड काम करता है यह बाएं से दाएं, ऊपर से नीचे तक, संभव प्रीिमेज के लिए पुनरावर्ती गहराई-पहली खोज में है। निम्नलिखित आरेख यह दर्शाता है कि किसी वर्तमान सेल के संभावित पड़ोस को देखते समय हमें कौन से पिछले पड़ोस पर विचार करना होगा:

f = free choice
h = only have to look at the neighborhood to the left
v = only have to look at the neighborhood to the top
b = have to look at both left and top neighborhoods

[f, h, h, h, h],
[v, b, b, b, b],
[v, b, b, b, b],
[v, b, b, b, b]

यहाँ कोड है:

def good_horizontal(left, right):
    if (left & 4) >> 2 != (right & 8) >> 3:
        return False
    if left & 1 != (right & 2) >> 1:
        return False
    return True


def good_vertical(bottom, top):
    if (bottom & 8) >> 3 != (top & 2) >> 1:
        return False
    if (bottom & 4) >> 2 != (top & 1):
        return False
    return True


ones = [1, 2, 4, 8]
zeros = [0, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15]
h = {}
v = {}

for i in range(16):
    h[i] = [j for j in range(16) if good_horizontal(i, j)]
    v[i] = [j for j in range(16) if good_vertical(i, j)]


def solve(arr):
    height = len(arr)
    width = len(arr[0])

    if height == 1 and width == 1:
        if arr[0][0] == 1:
            return 4
        else:
            return 12
    return solve_helper(arr)


def solve_helper(arr, i=0, j=0, partial=None):
    height = len(arr)
    width = len(arr[0])

    if arr[i][j] == 1:
        poss = ones
    else:
        poss = zeros

    if i == height - 1 and j == width - 1:  # We made it to the end of this chain
        if height == 1:
            return sum([1 for p in poss if p in h[partial[-1][-1]]])
        else:
            return sum([1 for p in poss if partial[-2][-1] in v[p] and p in h[partial[-1][-1]]])

    if j == width - 1:
        new_i, new_j = i + 1, 0
    else:
        new_i, new_j = i, j + 1

    if i == 0:
        if j == 0:
            # first call
            return sum([solve_helper(arr, new_i, new_j, [[p]]) for p in poss])
        # still in the first row
        return sum([solve_helper(arr, new_i, new_j, [partial[0] + [p]]) for p in poss if p in h[partial[0][-1]]])
    if j == 0:  # starting a new row
        return sum([solve_helper(arr, new_i, new_j, [r for r in partial + [[p]]]) for p in poss if partial[i - 1][0] in v[p]])
    return sum([solve_helper(arr, new_i, new_j, [r for r in partial[:-1] + ([partial[-1] + [p]])]) for p in poss if p in h[partial[i][-1]] and partial[i - 1][j] in v[p]])

इसे चलाने के लिए:

test3 = [
    [1, 1, 0, 1, 0, 1, 0, 1, 1, 0],
    [1, 1, 0, 0, 0, 0, 1, 1, 1, 0],
    [1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
    [0, 1, 0, 0, 0, 0, 1, 1, 0, 0]
]

expected3 = 11567

assert(solve(test3) == expected3)

1
छिपे हुए परीक्षण मामलों को करने में लंबा समय लग रहा है, इसलिए मैं यह सबमिशन नहीं कर रहा हूं। एक अलग एल्गोरिथ्म का प्रयास करें, क्योंकि इसमें समय की जटिलता बहुत अधिक है (यह O(<something>^n)मुझे लगता है।)
आर्टी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.