पूर्णांक आवरण को "पूर्ववत करें"


20

मैं एक दिलचस्प सैद्धांतिक समस्या में कई साल पहले भागा था। मुझे कभी कोई हल नहीं मिला, और जब मैं सोता हूं तो यह मुझे परेशान करता रहता है।

मान लीजिए कि आपके पास एक (C #) एप्लिकेशन है जो किसी संख्या में एक int, x नाम से रखती है। (X का मान निश्चित नहीं है)। जब प्रोग्राम चलाया जाता है, तो x को 33 से गुणा किया जाता है और फिर एक फाइल पर लिखा जाता है।

मूल स्रोत कोड इस तरह दिखता है:

int x = getSomeInt();
x = x * 33;
file.WriteLine(x); // Writes x to the file in decimal format

कुछ साल बाद, आपको पता चलता है कि आपको एक्स बैक के मूल मूल्यों की आवश्यकता है। कुछ गणनाएं सरल हैं: बस संख्या को फ़ाइल में 33 से विभाजित करें। हालांकि, अन्य मामलों में, एक्स काफी बड़ा है कि गुणन एक पूर्णांक अतिप्रवाह का कारण बना। डॉक्स के अनुसार , C # उच्च-क्रम बिट्स को छोटा कर देगा, जब तक कि संख्या से कम न हो int.MaxValue। क्या यह संभव है, इस मामले में, या तो:

  1. X को ही पुनर्प्राप्त करें या
  2. X के लिए संभावित मानों की सूची पुनर्प्राप्त करें?

यह मुझे लगता है (हालांकि मेरा तर्क निश्चित रूप से त्रुटिपूर्ण हो सकता है) कि एक या दोनों संभव होना चाहिए, क्योंकि अतिरिक्त कार्यों का सरल मामला (अनिवार्य रूप से यदि आप 10 से एक्स को जोड़ते हैं और इसे लपेटता है, तो आप 10 को घटा सकते हैं और एक्स के साथ फिर से हवा कर सकते हैं। ) और गुणन केवल बार-बार जोड़ा जाता है। साथ ही मदद करना (मेरा मानना ​​है) यह तथ्य है कि एक्स सभी मामलों में एक ही मूल्य से गुणा किया जाता है - एक निरंतर 33।

यह वर्षों से मेरी खोपड़ी पर अजीब क्षणों के आसपास नृत्य कर रहा है। यह मेरे साथ घटित होगा, मैं इसके माध्यम से सोचने की कोशिश में कुछ समय बिताऊंगा और फिर कुछ महीनों के लिए इसे भूल जाऊंगा। मैं इस समस्या का पीछा करते हुए थक गया हूँ! क्या कोई अंतर्दृष्टि प्रदान कर सकता है?

(साइड नोट: मैं वास्तव में यह कैसे टैग करना नहीं जानता। सुझावों का स्वागत है।)

संपादित करें: मुझे यह स्पष्ट करने दें कि यदि मुझे X के लिए संभावित मानों की सूची मिल सकती है, तो अन्य परीक्षण हैं जो मुझे मूल मूल्य को कम करने में मदद करने के लिए कर सकते हैं।



1
@rwong: आपकी टिप्पणी एकमात्र सही उत्तर है।
केविन क्लाइन

हाँ, और यूलर विधि विशेष रूप से प्रभावी लगता है के बाद से की गुणन mसिर्फ 2 ^ 32 या 2 ^ 64 है, प्लस के घातांक aसापेक्ष mसीधा है (बस की अनदेखी अतिप्रवाह वहाँ)
MSalters

1
मुझे लगता है कि विशेष समस्या वास्तव में तर्कसंगत पुनर्निर्माण
MSalters

1
@MSalters: नहीं, चलो तुम कहाँ है r*s^-1 mod mऔर आप दोनों को खोजने की जरूरत rहै और s। यहाँ, हम है r*s mod mऔर हम सब कुछ जानते हैं, लेकिन r
user2357112

जवाबों:


50

1041204193 से गुणा करें।

जब गुणा का परिणाम किसी int में फिट नहीं होता है, तो आपको सटीक परिणाम नहीं मिलेगा, लेकिन आपको सटीक परिणाम modulo 2 ** 32 के बराबर एक नंबर मिलेगा । इसका मतलब है कि यदि आपके द्वारा गुणा की गई संख्या को 2 ** 32 तक कॉपी किया गया था (जिसका अर्थ है कि यह विषम होना है), तो आप अपना नंबर वापस पाने के लिए इसके गुणक व्युत्क्रम से गुणा कर सकते हैं। वोल्फ्राम अल्फा या विस्तारित यूक्लिडियन एल्गोरिथ्म हमें 33 के गुणात्मक व्युत्क्रम मॉडुलो 2 ** 32 को 1041204193 बता सकता है। इसलिए, 1041204193 से गुणा करें, और आपके पास मूल एक्स बैक है।

अगर हम कहते हैं, 33 के बजाय 60, हम मूल संख्या को पुनर्प्राप्त करने में सक्षम नहीं होंगे, लेकिन हम इसे कुछ संभावनाओं तक सीमित कर पाएंगे। 60 को 4 * 15 में विभाजित करके, 15 mod 2 ** 32 के व्युत्क्रम की गणना करके, और उसके द्वारा गुणा करके, हम मूल संख्या से 4 गुना पुनर्प्राप्त कर सकते हैं, संख्या के केवल 2 उच्च-क्रम बिट्स को brute-force के लिए छोड़ देते हैं। वुल्फराम अल्फा हमें व्युत्क्रम के लिए 4008636143 देता है, जो एक इंट में फिट नहीं होता है, लेकिन यह ठीक है। हम सिर्फ 4008636143 मॉड 2 ** 32 के बराबर संख्या पाते हैं, या इसे किसी भी तरह से एक इंट में मजबूर करते हैं जो हमारे लिए कंपाइलर करता है, और परिणाम भी 15 मॉड 2 ** 32 का उलटा होगा। ( हमें -286331153 मिलता है। )


5
ओह यार। इसलिए मेरे कंप्यूटर ने मानचित्र बनाने का जो काम किया है, वह सभी पहले से ही यूक्लिड द्वारा किया गया था।
v010dya

21
मुझे आपके पहले वाक्य में बात-की-नेस अच्छी लगती है। "ओह, यह निश्चित रूप से 1041204193 है, क्या आपके पास यह याद नहीं है?" :
डोरकनॉ

2
यह एक जोड़े की संख्या के लिए काम करने का एक उदाहरण दिखाने के लिए उपयोगी होगा, जैसे कि जहां x * 33 अतिप्रवाह नहीं था और जहां यह किया था।
रोब वाट्स

2
होश उड़ जाना। वाह।
माइकल गाज़ोंडा

4
आपको 33 modulo $ 2 ^ {32} $ के विलोम को खोजने के लिए या तो यूक्लिड और न ही वोल्फ्रामअल्फा (निश्चित रूप से!) की आवश्यकता नहीं है। चूँकि $ x = 32 = 2 ^ 5 $ nilpotent है (ऑर्डर $ 7 $ का) modulo $ 2 ^ 32 $, आप सिर्फ ज्यामितीय श्रृंखला पहचान $ (1 + x) ^ {- 1} = 1-x + 1 ^ लागू कर सकते हैं 2-x ^ 3 + \ cdots + x ^ 6 $ (जिसके बाद श्रृंखला टूट जाती है) नंबर $ 33 ^ {- 1} = 1-2 ^ 5 + 2 ^ {10} -2 ^ {15} + खोजने के लिए। \ cdots + 2 ^ {30} $ जो $ 111110000011111000001111100001_2 = 1041204193_ {10} $ है।
मार्क वैन लीउवेन

6

यह शायद गणित (एसआईसी) एसई के प्रश्न के रूप में बेहतर अनुकूल है। आप मूल रूप से मॉड्यूलर अंकगणित के साथ काम कर रहे हैं, चूंकि बाएं-सबसे बिट्स को गिराना एक ही बात है।

मैं मैथ्स में उतना अच्छा नहीं हूं जितना लोग मैथ (सिक) एसई पर हैं, लेकिन मैं जवाब देने की कोशिश करूंगा।

हमारे पास यहां यह है कि संख्या 33 (3 * 11) से गुणा की जा रही है, और आपके मॉड के साथ इसका एकमात्र सामान्य भाजक है। 1. ऐसा इसलिए है क्योंकि परिभाषा में कंप्यूटर में बिट्स दो की शक्तियां हैं, और इस प्रकार आपका मॉड है दो की कुछ शक्ति।

आप उस तालिका का निर्माण करने में सक्षम होंगे जहां हर पिछले मूल्य के लिए आप निम्नलिखित मूल्य की गणना करते हैं। और सवाल यह हो जाता है कि निम्नलिखित संख्याएं केवल एक पिछले एक के अनुरूप हैं।

यदि यह 33 नहीं था, लेकिन एक प्रमुख या एक प्रमुख की कुछ शक्ति, मेरा मानना ​​है कि उत्तर हां होगा, लेकिन इस मामले में ... Math.SE पर पूछें!

प्रोग्रामेटिक टेस्ट

यह C ++ में है क्योंकि मुझे C # नहीं पता है, लेकिन अवधारणा अभी भी है। ऐसा लगता है कि आप कर सकते हैं:

#include <iostream>
#include <map>

int main(void)
{
    unsigned short count = 0;
    unsigned short x = 0;
    std::map<unsigned short, unsigned short> nextprev;

    nextprev[0] = 0;
    while(++x) nextprev[x] = 0;

    unsigned short nextX;
    while(++x)
    {
            nextX = x*33;
            if(nextprev[nextX])
            {
                    std::cout << nextprev[nextX] << "*33==" << nextX << " && " << x << "*33==" << nextX << std::endl;
                    ++count;
            }
            else
            {
                    nextprev[nextX] = x;
                    //std::cout << x << "*33==" << nextX << std::endl;
            }
    }

    std::cout << count << " collisions found" << std::endl;

    return 0;
}

इस तरह के मानचित्र को आबाद करने के बाद, यदि आप अगले को जानते हैं, तो आप हमेशा पिछले एक्स को प्राप्त करने में सक्षम होंगे। हर समय केवल एक ही मूल्य है।


एक गैर-नकारात्मक डेटाटाइप के साथ काम करना आसान क्यों होगा? कंप्यूटर में एक ही तरह से हस्ताक्षरित और अहस्ताक्षरित नहीं हैं, केवल उनका मानव आउटपुट प्रारूप अलग है?
बजे

@ Xcelled194 खैर, इन नंबरों के बारे में सोचना मेरे लिए आसान है।
v010dya

मेले पर्याप्त मानव कारक xD ~
Xcelled

मैंने इसे और अधिक स्पष्ट करने के लिए गैर-नकारात्मक के बारे में उस बयान को हटा दिया है।
v010dya

1
@ Xcelled194: असूचीबद्ध डेटाटाइप्स मॉड्यूलर अंकगणितीय के सामान्य नियमों का पालन करते हैं; हस्ताक्षरित प्रकार नहीं। विशेष रूप से, maxval+1केवल अहस्ताक्षरित प्रकारों के लिए है।
MSalters

2

इसे प्राप्त करने का एक तरीका जानवर बल का उपयोग करना है। क्षमा करें, मुझे C # नहीं पता है, लेकिन समाधान बताने के लिए निम्नलिखित c-like छद्म कोड है:

for (x=0; x<=INT_MAX; x++) {
    if (x*33 == test_value) {
        printf("%d\n", x);
    }
}

तकनीकी रूप से, आपको जिस चीज की आवश्यकता है, x*33%(INT_MAX+1) == test_valueलेकिन पूर्णांक ओवरफ़्लो स्वचालित रूप से %आपके लिए ऑपरेशन करेगा जब तक कि आपकी भाषा मनमाने ढंग से सटीक पूर्णांक (bigint) का उपयोग नहीं करती है।

इससे आपको जो संख्या मिलती है वह मूल संख्या हो सकती है। छपा पहला नंबर वह संख्या होगी जो ओवरफ्लो के एक दौर को उत्पन्न करेगा। दूसरी संख्या वह संख्या होगी जो ओवरफ्लो के दो दौर उत्पन्न करेगी। और इसी तरह..

इसलिए, यदि आप जानते हैं कि आप बेहतर डेटा देते हैं तो आप एक बेहतर अनुमान लगा सकते हैं। उदाहरण के लिए, सामान्य क्लॉक मैथ्स (हर 12 बजे के बाद ओवरफ्लो) पहले नंबर को और अधिक होने की संभावना रखते हैं क्योंकि ज्यादातर लोग आज हुई चीजों में रुचि रखते हैं।


C # मूल प्रकारों के साथ C की तरह व्यवहार करता है - यानी intएक 4 बाइट पर हस्ताक्षर किया गया पूर्णांक है जो लपेटता है, इसलिए आपका उत्तर अभी भी अच्छा है, हालांकि अगर आपके पास बहुत सारे इनपुट हैं तो ब्रूट फोर्सिंग सबसे अच्छा तरीका नहीं होगा! :)
Xcelled

हाँ, मैंने इसे यहाँ से modulo बीजगणित नियमों के साथ कागज़ पर करने की कोशिश की: math.stackexchange.com/questions/346271/… । लेकिन मैं यह पता लगाने की कोशिश में फंस गया और एक क्रूर बल समाधान के साथ समाप्त हो गया :)
स्लीपबेटमैन

दिलचस्प लेख, हालांकि मुझे इसे क्लिक करने के लिए गहराई से थोड़ा और अध्ययन करना होगा, मुझे लगता है।
23'14 को

@slebetman मेरे कोड को देखें। ऐसा लगता है कि केवल एक ही उत्तर है जब यह 33 से गुणा करने की बात आती है।
v010dya

2
सुधार: सी के intआसपास लपेटने की गारंटी नहीं है (अपने संकलक के डॉक्स देखें)। हालांकि यह अहस्ताक्षरित प्रकारों के लिए सही है।
थॉमस ईडिंग

1

आप SMT सॉल्वर Z3 को फॉर्मूले के लिए आपको एक संतोषजनक असाइनमेंट देने के लिए कह सकते हैं x * 33 = valueFromFile। यह आपके लिए उस समीकरण को उल्टा कर देगा और आपको हर संभव मूल्य प्रदान करेगा x। Z3 गुणा सहित सटीक बिटवेक्टर अंकगणित का समर्थन करता है।

    public static void InvertMultiplication()
    {
        int multiplicationResult = new Random().Next();
        int knownFactor = 33;

        using (var context = new Context(new Dictionary<string, string>() { { "MODEL", "true" } }))
        {
            uint bitvectorSize = 32;
            var xExpr = context.MkBVConst("x", bitvectorSize);
            var yExpr = context.MkBVConst("y", bitvectorSize);
            var mulExpr = context.MkBVMul(xExpr, yExpr);
            var eqResultExpr = context.MkEq(mulExpr, context.MkBV(multiplicationResult, bitvectorSize));
            var eqXExpr = context.MkEq(xExpr, context.MkBV(knownFactor, bitvectorSize));

            var solver = context.MkSimpleSolver();
            solver.Assert(eqResultExpr);
            solver.Assert(eqXExpr);

            var status = solver.Check();
            Console.WriteLine(status);
            if (status == Status.SATISFIABLE)
            {
                Console.WriteLine(solver.Model);
                Console.WriteLine("{0} * {1} = {2}", solver.Model.Eval(xExpr), solver.Model.Eval(yExpr), solver.Model.Eval(mulExpr));
            }
        }
    }

आउटपुट इस तरह दिखता है:

SATISFIABLE
(define-fun y () (_ BitVec 32)
  #xa33fec22)
(define-fun x () (_ BitVec 32)
  #x00000021)
33 * 2738875426 = 188575842

0

उस परिणाम को पूर्ववत् करने के लिए आपको एक गैर-शून्य परिमित राशि दी जाएगी (सामान्य रूप से अनंत, लेकिन intfin का एक सूक्ष्म उपसमूह है)। यदि यह यह स्वीकार्य है, तो बस संख्या उत्पन्न करें (अन्य उत्तर देखें)।

अन्यथा आपको चर के इतिहास के इतिहास (परिमित या अनंत लंबाई) की एक सूची बनाए रखने की आवश्यकता है।


0

हमेशा की तरह, एक वैज्ञानिक से एक समाधान और एक इंजीनियर से समाधान है।

ऊपर आपको एक वैज्ञानिक से एक बहुत अच्छा समाधान मिलेगा, जो हमेशा काम करता है, लेकिन आपको "गुणात्मक व्युत्क्रम" की गणना करने की आवश्यकता होती है।

यहां इंजीनियर का एक त्वरित समाधान है, जो आपको सभी संभव पूर्णांकों की कोशिश करने के लिए मजबूर नहीं करेगा।

val multiplier = 33 //used with 0x23456789
val problemAsLong = (-1947051863).toLong & 0xFFFFFFFFL

val overflowBit = 0x100000000L
for(test <- 0 until multiplier) {
  if((problemAsLong + overflowBit * test) % multiplier == 0) {
    val originalLong = (problemAsLong + overflowBit * test) / multiplier
    val original = originalLong.toInt
    println(s"$original (test = $test)")
  }
}

क्या विचार हैं?

  1. हम ओवरफ्लो हो गए, इसलिए आइए ठीक होने के लिए बड़े प्रकारों का उपयोग करें ( Int -> Long)
  2. हम शायद अतिप्रवाह के कारण कुछ बिट खो गए हैं, चलो उन्हें पुनर्प्राप्त करें
  3. ओवरफ्लो इससे ज्यादा नहीं था Int.MaxValue * multiplier

पूर्ण निष्पादन योग्य कोड http://ideone.com/zVMbGV पर स्थित है

विवरण:

  • val problemAsLong = (-1947051863).toLong & 0xFFFFFFFFL
    यहां हम अपने स्टोर किए गए नंबर को लॉन्ग में बदलते हैं, लेकिन चूंकि इंट और लॉन्ग साइन किए जाते हैं, इसलिए हमें इसे सही तरीके से करना होगा।
    इसलिए हम बिट वाइज और इंट के बिट्स का उपयोग करके संख्या को सीमित करते हैं।
  • val overflowBit = 0x100000000L
    यह बिट या इसका गुणन प्रारंभिक गुणन द्वारा खो सकता है।
    यह इंट रेंज के बाहर पहला बिट है।
  • for(test <- 0 until multiplier)
    तीसरे विचार के अनुसार अधिकतम अतिप्रवाह मल्टीप्लायर द्वारा सीमित है, इसलिए हम वास्तव में आवश्यकता से अधिक प्रयास न करें।
  • if((problemAsLong + overflowBit * test) % multiplier == 0)
    जाँच करें कि क्या संभवतः खोए हुए अतिप्रवाह को जोड़कर हम एक समाधान में आते हैं
  • val original = originalLong.toInt
    मूल समस्या Int रेंज में थी, तो चलिए इसे वापस करते हैं। अन्यथा हम गलत तरीके से संख्याओं को पुनर्प्राप्त कर सकते हैं, जो नकारात्मक थे।
  • println(s"$original (test = $test)")
    पहले समाधान के बाद मत तोड़ो, क्योंकि अन्य संभावित समाधान हो सकते हैं।

पुनश्च: तीसरा आइडिया सख्ती से सही नहीं है, लेकिन समझने योग्य होने के लिए इसे छोड़ दिया गया है।
Int.MaxValueहै 0x7FFFFFFF, लेकिन मैक्सिमम ओवरफ्लो है 0xFFFFFFFF * multiplier
इसलिए सही पाठ "अतिप्रवाह से अधिक नहीं था -1 * multiplier" होगा।
यह सही है, लेकिन हर कोई इसे नहीं समझेगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.