1 एमबी रैम के साथ 1 मिलियन 8-दशमलव-अंकों की संख्या को छांटना


726

मेरे पास 1 एमबी रैम और दूसरा कोई स्थानीय स्टोरेज वाला कंप्यूटर नहीं है। मुझे इसका उपयोग टीसीपी कनेक्शन पर 1 मिलियन 8-अंकीय दशमलव संख्याओं को स्वीकार करने के लिए करना चाहिए, उन्हें क्रमबद्ध करना होगा, और फिर छाँटे गए सूची को टीसीपी कनेक्शन पर भेजना होगा।

संख्याओं की सूची में डुप्लिकेट हो सकते हैं, जिन्हें मुझे त्यागना नहीं चाहिए। कोड को ROM में रखा जाएगा, इसलिए मुझे 1 MB से अपने कोड का आकार घटाने की आवश्यकता नहीं है। मेरे पास पहले से ही ईथरनेट पोर्ट को ड्राइव करने और टीसीपी / आईपी कनेक्शन को संभालने के लिए कोड है, और इसके स्टेट डेटा के लिए 2 केबी की आवश्यकता होती है, जिसमें 1 केबी बफर भी शामिल है जिसके माध्यम से कोड डेटा को पढ़ेगा और लिखेगा। क्या इस समस्या का कोई समाधान है?

प्रश्न और उत्तर के स्रोत:

slashdot.org

cleaton.net


45
एहम, एक मिलियन बार 8-अंकीय दशमलव संख्या (न्यूनतम 27-बिट पूर्णांक बाइनरी)> 1 एमबी रैम
Mr47

15
1M RAM का अर्थ है 2 ^ 20 बाइट्स? और इस वास्तुकला पर एक बाइट में कितने बिट्स हैं? और "1 मिलियन 8 अंकों की दशमलव संख्या" में "मिलियन" एक SI मिलियन (10 ^ 6) है? 8 अंक दशमलव संख्या, एक प्राकृतिक संख्या <10 ^ 8, एक परिमेय संख्या है जिसका दशमलव प्रतिनिधित्व दशमलव बिंदु को छोड़कर 8 अंक लेता है, या कुछ और?

13
1 मिलियन 8 दशमलव अंक या 1 मिलियन 8 बिट संख्या?
पैट्रिक व्हाइट

13
यह मुझे "डॉ। डॉब्स जर्नल" (कहीं-कहीं 1998-2001 के बीच) के एक लेख की याद दिलाता है, जहाँ लेखक ने फ़ोन नंबरों को छाँटने के लिए एक प्रविष्टि प्रकार का उपयोग किया था क्योंकि वह उन्हें पढ़ रहा था: पहली बार मुझे एहसास हुआ था कि, कभी-कभी, एक धीमा एल्गोरिथम तेज हो सकता है ...
एड्रियन प्लिसन

103
एक और समाधान है जिसका किसी ने अभी तक उल्लेख नहीं किया है: 2 एमबी रैम के साथ हार्डवेयर खरीदें। यह बहुत अधिक महंगा नहीं होना चाहिए, और यह समस्या को बहुत आसान बना देगा, हल करने के लिए बहुत आसान है।
डैनियल वैगनर

जवाबों:


716

यहाँ एक नहीं बल्कि डरपोक चाल का उल्लेख है। हम मानते हैं कि आपके पास डेटा संग्रहीत करने का कोई अतिरिक्त तरीका नहीं है, लेकिन यह कड़ाई से सच नहीं है।

आपकी समस्या का एक तरीका यह है कि निम्नलिखित भयानक काम करें, जिसे किसी भी परिस्थिति में किसी के द्वारा प्रयास नहीं किया जाना चाहिए: डेटा को संग्रहीत करने के लिए नेटवर्क ट्रैफ़िक का उपयोग करें। और नहीं, मेरा मतलब NAS नहीं है।

आप निम्नलिखित तरीकों से केवल कुछ बाइट्स रैम के साथ संख्याओं को सॉर्ट कर सकते हैं:

  • पहले 2 चर लें: COUNTERऔर VALUE
  • पहले सभी रजिस्टरों को सेट करें 0;
  • हर बार जब आप एक पूर्णांक प्राप्त I, वेतन वृद्धि COUNTERऔर सेट VALUEकरने के लिए max(VALUE, I);
  • फिर Iराउटर पर डेटा सेट के साथ एक ICMP इको रिक्वेस्ट पैकेट भेजें । मिटाओ Iऔर दोहराओ।
  • हर बार जब आप लौटे हुए ICMP पैकेट को प्राप्त करते हैं, तो आप केवल पूर्णांक को निकालते हैं और इसे एक और प्रतिध्वनि अनुरोध में वापस भेजते हैं। यह ICMP अनुरोधों की एक बड़ी संख्या को पिछड़े और पूर्णांक युक्त अग्रेषित करता है।

एक बार COUNTERपहुंचने के बाद 1000000, आपके पास ICMP अनुरोधों की निरंतर स्ट्रीम में संग्रहीत सभी मान हैं, और VALUEअब इसमें अधिकतम पूर्णांक शामिल है। कुछ उठाओ threshold T >> 1000000COUNTERशून्य पर सेट करें । हर बार जब आप एक ICMP पैकेट प्राप्त करते हैं, वृद्धि करते हैं COUNTERऔर निहित पूर्णांक Iको एक और प्रतिध्वनि अनुरोध में वापस भेजते हैं, जब तक कि I=VALUEकिस मामले में यह सॉर्ट किए गए पूर्णांकों के लिए गंतव्य तक नहीं पहुंचता है। एक बार COUNTER=T, घटती VALUEद्वारा 1, रीसेट COUNTERशून्य और दोहराने के लिए। एक बार VALUEशून्य तक पहुंचने के बाद आपको सबसे बड़े से लेकर सबसे छोटे गंतव्य तक सभी पूर्णांक संचारित करने चाहिए, और दो स्थिर चर (और अस्थायी मूल्यों के लिए आपको जो भी छोटी राशि की आवश्यकता होती है) के लिए केवल 47 बिट्स रैम का उपयोग किया है।

मुझे पता है कि यह भयानक है, और मुझे पता है कि सभी प्रकार के व्यावहारिक मुद्दे हो सकते हैं, लेकिन मैंने सोचा कि यह आप में से कुछ को हँसा सकता है या कम से कम आपको भयभीत कर सकता है।


27
तो आप मूल रूप से नेटवर्क विलंबता का लाभ उठा रहे हैं और अपने राउटर को एक तरह की कतार में बदल रहे हैं?
एरिक आर।

335
यह समाधान बॉक्स के ठीक बाहर नहीं है; ऐसा लगता है कि घर पर इसके बॉक्स को भूल गए हैं: D
व्लादिस्लाव ज़ोरोव

28
शानदार जवाब ... मुझे ये जवाब पसंद हैं क्योंकि वे वास्तव में यह उजागर करते हैं कि किसी समस्या का समाधान कितना अलग हो सकता है
StackOverflowed

33
ICMP विश्वसनीय नहीं है।
स्लीपलेसनरड

13
@ मरमरा: आप शीर्ष पर सही नोटिस करेंगे, मैं कहता हूं "आपकी समस्या का एक तरीका निम्नलिखित भयानक काम करना है, जिसे किसी भी परिस्थिति में किसी के द्वारा प्रयास नहीं किया जाना चाहिए"। एक कारण था कि मैंने यह कहा।
जो फिट्ज़सिमों

423

यहाँ कुछ कार्य C ++ कोड है जो इस समस्या को हल करता है।

स्मृति बाधाओं से संतुष्ट हैं कि सबूत:

संपादक: इस पोस्ट में या उनके ब्लॉग में लेखक द्वारा दी गई अधिकतम मेमोरी आवश्यकताओं का कोई प्रमाण नहीं है। चूंकि किसी मान को एन्कोड करने के लिए आवश्यक बिट्स की संख्या पहले से एन्कोड किए गए मानों पर निर्भर करती है, इस तरह के प्रमाण की संभावना गैर-तुच्छ है। लेखक नोट करता है कि सबसे बड़ा एन्कोडेड आकार वह अनुभवजन्य रूप से ठोकर खा सकता था 1011732, और बफर आकार को 1013000मनमाने ढंग से चुना ।

typedef unsigned int u32;

namespace WorkArea
{
    static const u32 circularSize = 253250;
    u32 circular[circularSize] = { 0 };         // consumes 1013000 bytes

    static const u32 stageSize = 8000;
    u32 stage[stageSize];                       // consumes 32000 bytes

    ...

एक साथ, ये दो सरणियाँ 1045000 बाइट्स का भंडारण करती हैं। शेष चर और स्टैक स्थान के लिए 1048576 - 1045000 - 2 × 1024 = 1528 बाइट्स निकलता है।

यह मेरे Xeon W3520 पर लगभग 23 सेकंड में चलता है। आप यह सत्यापित कर सकते हैं कि प्रोग्राम निम्नलिखित पायथन स्क्रिप्ट का उपयोग करके काम करता है, प्रोग्राम का नाम मानकर sort1mb.exe

from subprocess import *
import random

sequence = [random.randint(0, 99999999) for i in xrange(1000000)]

sorter = Popen('sort1mb.exe', stdin=PIPE, stdout=PIPE)
for value in sequence:
    sorter.stdin.write('%08d\n' % value)
sorter.stdin.close()

result = [int(line) for line in sorter.stdout]
print('OK!' if result == sorted(sequence) else 'Error!')

एल्गोरिथ्म का एक विस्तृत विवरण निम्न पदों की श्रृंखला में पाया जा सकता है:


8
@preshing हाँ, हम इसके बारे में विस्तृत विवरण चाहते हैं।
T सूद

25
मुझे लगता है कि मुख्य अवलोकन यह है कि एक 8-अंकीय संख्या में लगभग 26.6 बिट्स की जानकारी है और एक मिलियन 19.9 बिट्स हैं। यदि आप सूची को संकुचित करते हैं (आसन्न मूल्यों के अंतर को स्टोर करते हैं) तो अंतर 0 (0 बिट्स) से 99999999 (26.6 बिट्स) तक होता है, लेकिन आपके पास प्रत्येक जोड़ी के बीच अधिकतम डेल्टा नहीं हो सकता है । सबसे खराब स्थिति वास्तव में एक मिलियन समान रूप से वितरित मूल्यों की होनी चाहिए, जिसमें डेल्टा (26.6-19.9) या डेल्टा के बारे में 6.7 बिट्स की आवश्यकता होती है। 6.7 बिट्स का एक मिलियन मान आसानी से 1M में जमा हो जाता है। डेल्टा कंप्रेशन को लगातार मर्ज करने की आवश्यकता होती है ताकि आप लगभग मुफ्त में पा सकें।
बेन जैक्सन

4
मीठा घोल। तुम सब लोग स्पष्टीकरण के लिए अपने ब्लॉग की यात्रा करनी चाहिए preshing.com/20121025/...
davec

9
@BenJackson: आपके गणित में कहीं न कहीं कोई त्रुटि है। 2.265 x 10 ^ 2436455 अनूठे संभावित आउटपुट (10 ^ 6 8-अंकीय पूर्णांक के सेट के आदेश) हैं, जो स्टोर करने के लिए 8.094 x 10 ^ 6 बिट्स लेता है (यानी मेगाबाइट के नीचे एक बाल)। कोई भी चतुर योजना बिना नुकसान के इस सूचना सिद्धांत सीमा से परे नहीं जा सकती। आपके स्पष्टीकरण का अर्थ है कि आपको बहुत कम जगह की आवश्यकता है, और इसलिए गलत है। दरअसल, उपरोक्त समाधान में "परिपत्र" आवश्यक जानकारी रखने के लिए सिर्फ इतना बड़ा है, इसलिए लगता है कि प्रेसिंग ने इसे ध्यान में रखा है, लेकिन आप इसे याद कर रहे हैं।
जो फिट्जसिमंस

5
@JoeFitzsimons: मैंने पुनरावर्तन का काम नहीं किया था (0..m से n नंबरों का अनूठा सॉर्ट किया गया सेट (n+m)!/(n!m!)) इसलिए आपको सही होना चाहिए। संभवतः यह मेरा अनुमान है कि बी बिट्स का एक डेल्टा स्टोर करने के लिए बी बिट्स लेता है - स्पष्ट रूप से 0 का डेल्टा स्टोर करने के लिए 0 बिट्स नहीं लेता है।
बेन जैक्सन

371

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

यह काफी दिलचस्प काम है और यहां एक और समाधान है। मुझे आशा है कि किसी को परिणाम उपयोगी (या कम से कम दिलचस्प) मिलेगा।

चरण 1: प्रारंभिक डेटा संरचना, किसी न किसी संपीड़न दृष्टिकोण, बुनियादी परिणाम

चलो कुछ सरल गणित करते हैं: हमारे पास 1M (1048576 बाइट्स) है जो शुरू में 10 ^ 6 8 अंकों की दशमलव संख्याओं को संग्रहीत करने के लिए उपलब्ध है। [0; 99,999,999]। तो एक नंबर को स्टोर करने के लिए 27 बिट्स की जरूरत होती है (यह मानकर कि अहस्ताक्षरित संख्याओं का उपयोग किया जाएगा)। इस प्रकार, एक कच्चे स्ट्रीम को स्टोर करने के लिए ~ 3.5M RAM की आवश्यकता होगी। किसी ने पहले ही कहा था कि यह संभव नहीं है, लेकिन मैं कहूंगा कि यदि इनपुट "अच्छा पर्याप्त" है तो कार्य को हल किया जा सकता है। असल में, यह विचार है कि कम्प्रेशन फ़ैक्टर 0.29 या उच्चतर के साथ इनपुट डेटा को संपीड़ित करना और उचित तरीके से सॉर्ट करना है।

चलो पहले संपीड़न मुद्दे को हल करें। कुछ प्रासंगिक परीक्षण पहले से ही उपलब्ध हैं:

http://www.theeggeadventure.com/wikimedia/index.php/Java_Data_Compression

"मैंने संपीड़न के विभिन्न रूपों का उपयोग करके एक मिलियन लगातार पूर्णांकों को संपीड़ित करने के लिए एक परीक्षण चलाया। परिणाम निम्नानुसार हैं:"

None     4000027
Deflate  2006803
Filtered 1391833
BZip2    427067
Lzma     255040

ऐसा लगता है कि LZMA ( Lempel-Ziv-Markov श्रृंखला एल्गोरिथ्म ) जारी रखने के लिए एक अच्छा विकल्प है। मैंने एक साधारण PoC तैयार किया है, लेकिन अभी भी कुछ विवरणों पर प्रकाश डाला जाना है:

  1. मेमोरी सीमित है इसलिए यह विचार है कि संख्याओं को निर्धारित किया जाए और अस्थायी भंडारण के रूप में संपीड़ित बाल्टी (गतिशील आकार) का उपयोग किया जाए
  2. निर्धारित डेटा के साथ एक बेहतर संपीड़न कारक प्राप्त करना आसान है, इसलिए प्रत्येक बाल्टी के लिए एक स्थिर बफर है (बफर से संख्या LZMA से पहले सॉर्ट की जानी है)
  3. प्रत्येक बाल्टी एक विशिष्ट रेंज रखती है, इसलिए प्रत्येक बाल्टी के लिए अलग-अलग अंतिम रूप दिया जा सकता है
  4. बाल्टी का आकार ठीक से सेट किया जा सकता है, इसलिए संग्रहीत डेटा को डिकम्प्रेस करने के लिए पर्याप्त मेमोरी होगी और प्रत्येक बाल्टी के लिए अलग से अंतिम प्रकार करना होगा

इन-मेमोरी सॉर्टिंग

कृपया ध्यान दें, संलग्न कोड एक पीओसी है , इसे अंतिम समाधान के रूप में इस्तेमाल नहीं किया जा सकता है, यह सिर्फ कुछ छोटे बफ़र्स का उपयोग करके कुछ इष्टतम तरीके (संभवतः संपीड़ित) में संख्याओं को संग्रहीत करने के विचार को प्रदर्शित करता है। LZMA अंतिम समाधान के रूप में प्रस्तावित नहीं है। यह इस PoC के लिए एक संपीड़न शुरू करने के लिए सबसे तेज़ संभव तरीके के रूप में प्रयोग किया जाता है।

नीचे PoC कोड देखें (कृपया इसे केवल एक डेमो नोट करें, इसे LZMA-Java की आवश्यकता होगी):

public class MemorySortDemo {

static final int NUM_COUNT = 1000000;
static final int NUM_MAX   = 100000000;

static final int BUCKETS      = 5;
static final int DICT_SIZE    = 16 * 1024; // LZMA dictionary size
static final int BUCKET_SIZE  = 1024;
static final int BUFFER_SIZE  = 10 * 1024;
static final int BUCKET_RANGE = NUM_MAX / BUCKETS;

static class Producer {
    private Random random = new Random();
    public int produce() { return random.nextInt(NUM_MAX); }
}

static class Bucket {
    public int size, pointer;
    public int[] buffer = new int[BUFFER_SIZE];

    public ByteArrayOutputStream tempOut = new ByteArrayOutputStream();
    public DataOutputStream tempDataOut = new DataOutputStream(tempOut);
    public ByteArrayOutputStream compressedOut = new ByteArrayOutputStream();

    public void submitBuffer() throws IOException {
        Arrays.sort(buffer, 0, pointer);

        for (int j = 0; j < pointer; j++) {
            tempDataOut.writeInt(buffer[j]);
            size++;
        }            
        pointer = 0;
    }

    public void write(int value) throws IOException {
        if (isBufferFull()) {
            submitBuffer();
        }
        buffer[pointer++] = value;
    }

    public boolean isBufferFull() {
        return pointer == BUFFER_SIZE;
    }

    public byte[] compressData() throws IOException {
        tempDataOut.close();
        return compress(tempOut.toByteArray());
    }        

    private byte[] compress(byte[] input) throws IOException {
        final BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(input));
        final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(compressedOut));

        final Encoder encoder = new Encoder();
        encoder.setEndMarkerMode(true);
        encoder.setNumFastBytes(0x20);
        encoder.setDictionarySize(DICT_SIZE);
        encoder.setMatchFinder(Encoder.EMatchFinderTypeBT4);

        ByteArrayOutputStream encoderPrperties = new ByteArrayOutputStream();
        encoder.writeCoderProperties(encoderPrperties);
        encoderPrperties.flush();
        encoderPrperties.close();

        encoder.code(in, out, -1, -1, null);
        out.flush();
        out.close();
        in.close();

        return encoderPrperties.toByteArray();
    }

    public int[] decompress(byte[] properties) throws IOException {
        InputStream in = new ByteArrayInputStream(compressedOut.toByteArray());
        ByteArrayOutputStream data = new ByteArrayOutputStream(10 * 1024);
        BufferedOutputStream out = new BufferedOutputStream(data);

        Decoder decoder = new Decoder();
        decoder.setDecoderProperties(properties);
        decoder.code(in, out, 4 * size);

        out.flush();
        out.close();
        in.close();

        DataInputStream input = new DataInputStream(new ByteArrayInputStream(data.toByteArray()));
        int[] array = new int[size];
        for (int k = 0; k < size; k++) {
            array[k] = input.readInt();
        }

        return array;
    }
}

static class Sorter {
    private Bucket[] bucket = new Bucket[BUCKETS];

    public void doSort(Producer p, Consumer c) throws IOException {

        for (int i = 0; i < bucket.length; i++) {  // allocate buckets
            bucket[i] = new Bucket();
        }

        for(int i=0; i< NUM_COUNT; i++) {         // produce some data
            int value = p.produce();
            int bucketId = value/BUCKET_RANGE;
            bucket[bucketId].write(value);
            c.register(value);
        }

        for (int i = 0; i < bucket.length; i++) { // submit non-empty buffers
            bucket[i].submitBuffer();
        }

        byte[] compressProperties = null;
        for (int i = 0; i < bucket.length; i++) { // compress the data
            compressProperties = bucket[i].compressData();
        }

        printStatistics();

        for (int i = 0; i < bucket.length; i++) { // decode & sort buckets one by one
            int[] array = bucket[i].decompress(compressProperties);
            Arrays.sort(array);

            for(int v : array) {
                c.consume(v);
            }
        }
        c.finalCheck();
    }

    public void printStatistics() {
        int size = 0;
        int sizeCompressed = 0;

        for (int i = 0; i < BUCKETS; i++) {
            int bucketSize = 4*bucket[i].size;
            size += bucketSize;
            sizeCompressed += bucket[i].compressedOut.size();

            System.out.println("  bucket[" + i
                    + "] contains: " + bucket[i].size
                    + " numbers, compressed size: " + bucket[i].compressedOut.size()
                    + String.format(" compression factor: %.2f", ((double)bucket[i].compressedOut.size())/bucketSize));
        }

        System.out.println(String.format("Data size: %.2fM",(double)size/(1014*1024))
                + String.format(" compressed %.2fM",(double)sizeCompressed/(1014*1024))
                + String.format(" compression factor %.2f",(double)sizeCompressed/size));
    }
}

static class Consumer {
    private Set<Integer> values = new HashSet<>();

    int v = -1;
    public void consume(int value) {
        if(v < 0) v = value;

        if(v > value) {
            throw new IllegalArgumentException("Current value is greater than previous: " + v + " > " + value);
        }else{
            v = value;
            values.remove(value);
        }
    }

    public void register(int value) {
        values.add(value);
    }

    public void finalCheck() {
        System.out.println(values.size() > 0 ? "NOT OK: " + values.size() : "OK!");
    }
}

public static void main(String[] args) throws IOException {
    Producer p = new Producer();
    Consumer c = new Consumer();
    Sorter sorter = new Sorter();

    sorter.doSort(p, c);
}
}

यादृच्छिक संख्याओं के साथ यह निम्नलिखित उत्पादन करता है:

bucket[0] contains: 200357 numbers, compressed size: 353679 compression factor: 0.44
bucket[1] contains: 199465 numbers, compressed size: 352127 compression factor: 0.44
bucket[2] contains: 199682 numbers, compressed size: 352464 compression factor: 0.44
bucket[3] contains: 199949 numbers, compressed size: 352947 compression factor: 0.44
bucket[4] contains: 200547 numbers, compressed size: 353914 compression factor: 0.44
Data size: 3.85M compressed 1.70M compression factor 0.44

एक साधारण आरोही क्रम (एक बाल्टी का उपयोग किया जाता है) के लिए यह उत्पादन करता है:

bucket[0] contains: 1000000 numbers, compressed size: 256700 compression factor: 0.06
Data size: 3.85M compressed 0.25M compression factor 0.06

संपादित करें

निष्कर्ष:

  1. प्रकृति को बेवकूफ बनाने की कोशिश मत करो
  2. कम स्मृति पदचिह्न के साथ सरल संपीड़न का उपयोग करें
  3. कुछ अतिरिक्त सुराग वास्तव में आवश्यक हैं। आम बुलेट प्रूफ समाधान संभव नहीं लगता है।

स्टेज 2: संवर्धित संपीड़न, अंतिम निष्कर्ष

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

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

एन्कोडिंग योजना

यादृच्छिक इनपुट परीक्षण थोड़ा बेहतर परिणाम दिखाता है:

bucket[0] contains: 10103 numbers, compressed size: 13683 compression factor: 0.34
bucket[1] contains: 9885 numbers, compressed size: 13479 compression factor: 0.34
...
bucket[98] contains: 10026 numbers, compressed size: 13612 compression factor: 0.34
bucket[99] contains: 10058 numbers, compressed size: 13701 compression factor: 0.34
Data size: 3.85M compressed 1.31M compression factor 0.34

नमूना कोड

  public static void encode(int[] buffer, int length, BinaryOut output) {
    short size = (short)(length & 0x7FFF);

    output.write(size);
    output.write(buffer[0]);

    for(int i=1; i< size; i++) {
        int next = buffer[i] - buffer[i-1];
        int bits = getBinarySize(next);

        int len = bits;

        if(bits > 24) {
          output.write(3, 2);
          len = bits - 24;
        }else if(bits > 16) {
          output.write(2, 2);
          len = bits-16;
        }else if(bits > 8) {
          output.write(1, 2);
          len = bits - 8;
        }else{
          output.write(0, 2);
        }

        if (len > 0) {
            if ((len % 2) > 0) {
                len = len / 2;
                output.write(len, 2);
                output.write(false);
            } else {
                len = len / 2 - 1;
                output.write(len, 2);
            }

            output.write(next, bits);
        }
    }
}

public static short decode(BinaryIn input, int[] buffer, int offset) {
    short length = input.readShort();
    int value = input.readInt();
    buffer[offset] = value;

    for (int i = 1; i < length; i++) {
        int flag = input.readInt(2);

        int bits;
        int next = 0;
        switch (flag) {
            case 0:
                bits = 2 * input.readInt(2) + 2;
                next = input.readInt(bits);
                break;
            case 1:
                bits = 8 + 2 * input.readInt(2) +2;
                next = input.readInt(bits);
                break;
            case 2:
                bits = 16 + 2 * input.readInt(2) +2;
                next = input.readInt(bits);
                break;
            case 3:
                bits = 24 + 2 * input.readInt(2) +2;
                next = input.readInt(bits);
                break;
        }

        buffer[offset + i] = buffer[offset + i - 1] + next;
    }

   return length;
}

कृपया ध्यान दें, यह दृष्टिकोण:

  1. बहुत अधिक मेमोरी का उपभोग नहीं करता है
  2. धाराओं के साथ काम करता है
  3. इतना बुरा परिणाम नहीं प्रदान करता है

पूर्ण कोड यहां पाया जा सकता है , बाइनरीइंपुट और बाइनरीऑटपुट कार्यान्वयन यहां पाया जा सकता है

अंतिम निष्कर्ष

कोई अंतिम निष्कर्ष नहीं: :) कभी-कभी मेटा-स्तर के दृष्टिकोण से एक स्तर को ऊपर ले जाने और कार्य की समीक्षा करना वास्तव में अच्छा विचार है ।

इस कार्य के साथ कुछ समय बिताना मजेदार था। BTW, नीचे कई दिलचस्प जवाब हैं। आपका ध्यान और खुश कोडिंग के लिए धन्यवाद।


17
मैंने इंकस्केप का इस्तेमाल किया । वैसे महान उपकरण। आप एक उदाहरण के रूप में इस आरेख स्रोत का उपयोग कर सकते हैं ।
रेनाट गिलमनोव

21
निश्चित रूप से LZMA को इस मामले में उपयोगी होने के लिए बहुत अधिक मेमोरी की आवश्यकता है? एक एल्गोरिथ्म के रूप में इसका मतलब है कि स्मृति में कुशल होने के बजाय संग्रहीत या प्रसारित किए जाने वाले डेटा की मात्रा को कम करना।
मेजीग

67
यह बकवास है ... 1 मिलियन यादृच्छिक 27 बिट पूर्णांक प्राप्त करें, उन्हें सॉर्ट करें, 7zip, xz के साथ संपीड़ित करें, जो LZMA चाहते हैं। परिणाम 1 एमबी से अधिक है। शीर्ष पर आधार अनुक्रमिक संख्याओं का संपीड़न है। 0bit के साथ डेल्टा एन्कोडिंग सिर्फ संख्या होगी, जैसे 1000000 (4 बाइट्स में कहें)। अनुक्रमिक और डुप्लिकेट (कोई अंतराल) के साथ, संख्या 1000000 और 1000000 बिट्स = 128 केबी, डुप्लिकेट संख्या के लिए 0 और 1 के साथ अगले को चिह्नित करने के लिए। जब आपके पास यादृच्छिक अंतराल होते हैं, तो भी छोटा, LZMA हास्यास्पद है। इसे इसके लिए नहीं बनाया गया है।
एलेको

30
यह वास्तव में काम नहीं करेगा। मैंने एक सिमुलेशन चलाया और जबकि संपीड़ित डेटा 1 एमबी (लगभग 1.5 एमबी) से अधिक है, यह अभी भी डेटा को संपीड़ित करने के लिए 100 एमबी से अधिक रैम का उपयोग करता है। तो भी संपीड़ित पूर्णांक समस्या को चलाने के समय रैम उपयोग का उल्लेख नहीं करने के लिए फिट नहीं है। आपको इनाम देना स्टैकओवरफ्लो पर मेरी सबसे बड़ी त्रुटि है।
पसंदीदा onwuemene

10
इस उत्तर को बहुत अधिक उकेरा गया है क्योंकि बहुत सारे प्रोग्रामर सिद्ध कोड के बजाय चमकदार विचारों को पसंद करते हैं। यदि यह विचार काम करता है, तो आप एक वास्तविक संपीड़न एल्गोरिथ्म को केवल एक दावे के बजाय चुने गए और सिद्ध होते देखेंगे जो निश्चित रूप से वहाँ है जो इसे कर सकता है ... जब यह बहुत संभव है कि वहाँ कोई ऐसा नहीं है जो यह कर सकता है ।
ओलाथे

185

एक समाधान केवल 1 मेगाबाइट और 1 मिलियन बाइट्स के बीच अंतर के कारण संभव है। अनुमति के बारे में 2 से बिजली 8093729.5 अलग-अलग तरीके हैं 1 मिलियन 8-अंकीय संख्याओं को चुनने के लिए डुप्लिकेट अनुमत और आदेश महत्वहीन है, इसलिए केवल 1 मिलियन बाइट के साथ एक मशीन में सभी संभावनाओं का प्रतिनिधित्व करने के लिए पर्याप्त राज्य नहीं हैं। लेकिन 1M (टीसीपी / आईपी के लिए कम 2k) 1022 * 1024 * 8 = 8372224 बिट्स है, इसलिए एक समाधान संभव है।

भाग 1, प्रारंभिक समाधान

इस दृष्टिकोण को 1M से थोड़ा अधिक की आवश्यकता है, मैं इसे 1M में बाद में फिट करने के लिए परिष्कृत करूंगा।

मैं ० से ९९९९९९ में numbers-बिट नंबरों के सब्लिस्ट के अनुक्रम में संख्याओं की एक कॉम्पैक्ट सॉर्ट की गई सूची को संग्रहीत करूंगा। पहली सबलिस्ट 0 से 127 तक की संख्या रखती है, दूसरी सबलिस्ट 128 से 255 तक की संख्या रखती है, आदि 100000000/128 बिल्कुल 781250 है, इसलिए 781250 ऐसे सब्लिस्ट की आवश्यकता होगी।

प्रत्येक सबलिस्ट में एक 2-बिट सबलिस्ट हेडर होता है, उसके बाद एक सब-लिस्ट बॉडी होती है। सबलिस्ट बॉडी में 7 बिट प्रति सब्बल एंट्री होती है। सब्लिस्ट्स को एक साथ समेटा जाता है, और प्रारूप यह बताना संभव बनाता है कि एक सबलिस्ट कहां समाप्त होता है और अगला शुरू होता है। पूरी तरह से आबादी वाली सूची के लिए आवश्यक कुल संग्रहण 2 * 781250 + 7 * 1000000 = 8562500 बिट्स है, जो लगभग 1.021 एम-बाइट्स है।

4 संभावित सबलिस्ट हेडर मान हैं:

00 खाली सबलिस्ट, कुछ भी नहीं है।

01 सिंगलटन, सबलिस्ट में केवल एक प्रविष्टि है और अगले 7 बिट्स इसे पकड़ते हैं।

10 सबलिस्ट में कम से कम 2 अलग-अलग संख्याएँ होती हैं। प्रविष्टियों को गैर-घटते क्रम में संग्रहीत किया जाता है, सिवाय इसके कि अंतिम प्रविष्टि पहले की तुलना में कम या बराबर है। यह सबलिस्ट के अंत की पहचान करने की अनुमति देता है। उदाहरण के लिए, संख्या 2,4,6 को (4,6,2) के रूप में संग्रहीत किया जाएगा। संख्या 2,2,3,4,4 को (2,3,4,4,2) के रूप में संग्रहीत किया जाएगा।

11 सबलिस्ट एक संख्या के 2 या अधिक दोहराव रखती है। अगले 7 बिट नंबर देते हैं। फिर मान 1 के साथ शून्य या अधिक 7-बिट प्रविष्टियां आते हैं, जिसके बाद मान 0. के साथ 7-बिट प्रविष्टि होती है। सबलिस्ट बॉडी की लंबाई दोहराव की संख्या निर्धारित करती है। उदाहरण के लिए, संख्या 12,12 को (12,0) के रूप में संग्रहीत किया जाएगा, संख्या 12,12,12 को (12,1,0), 12,12,12 के रूप में संग्रहीत किया जाएगा (12,1) , 1,0) और इसी तरह।

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

नीचे दी गई सूची सूची मर्ज ऑपरेशन की शुरुआत से ठीक पहले मेमोरी का प्रतिनिधित्व करती है। "O" वह क्षेत्र है जो 32-बिट पूर्णांक को सॉर्ट करता है। "X" क्षेत्र पुरानी कॉम्पैक्ट सूची रखने वाले क्षेत्र हैं। "=" संकेत कॉम्पैक्ट सूची के लिए विस्तार कक्ष हैं, "ओ" एस में प्रत्येक पूर्णांक के लिए 7 बिट्स हैं। "Z" अन्य यादृच्छिक ओवरहेड हैं।

ZZZOOOOOOOOOOOOOOOOOOOOOOOOOO==========XXXXXXXXXXXXXXXXXXXXXXXXXX

मर्ज दिनचर्या बाईं ओर "O" और सबसे बाईं ओर "X" पढ़ना शुरू करती है, और बाईं ओर "=" लिखना शुरू करती है। जब तक सभी नए पूर्णांक नहीं मिल जाते, तब तक लिखने वाला पॉइंटर कॉम्पेक्ट लिस्ट पॉइंटर को नहीं पकड़ता, क्योंकि दोनों पॉइंटर्स प्रत्येक सबलिस्ट के लिए 2 बिट्स एडवांस करते हैं और पुरानी कॉम्पेक्ट लिस्ट में प्रत्येक एंट्री के लिए 7 बिट्स होते हैं, और इसके लिए पर्याप्त अतिरिक्त जगह होती है। नए नंबरों के लिए 7-बिट प्रविष्टियां।

भाग 2, यह 1M में cramming

1M में उपरोक्त समाधान को निचोड़ने के लिए, मुझे कॉम्पैक्ट सूची प्रारूप को थोड़ा और अधिक कॉम्पैक्ट बनाने की आवश्यकता है। मैं एक सबलिस्ट प्रकार से छुटकारा पाऊंगा, जिससे कि केवल 3 अलग-अलग संभव सबलिस्ट हेडर मान होंगे। तब मैं "00", "01" और "1" को सबलिस्ट हेडर मान के रूप में उपयोग कर सकता हूं और कुछ बिट्स को बचा सकता हूं। निम्न प्रकार हैं:

एक खाली सबलिस्ट, कुछ भी नहीं है।

बी सिंगलटन, सबलिस्ट में केवल एक प्रविष्टि है और अगले 7 बिट्स इसे पकड़ते हैं।

C उपसूची में कम से कम 2 अलग-अलग संख्याएँ हैं। प्रविष्टियों को गैर-घटते क्रम में संग्रहीत किया जाता है, सिवाय इसके कि अंतिम प्रविष्टि पहले की तुलना में कम या बराबर है। यह सबलिस्ट के अंत की पहचान करने की अनुमति देता है। उदाहरण के लिए, 2,4,6 की संख्या (4,6,2) के रूप में संग्रहीत की जाएगी। संख्या 2,2,3,4,4 को (2,3,4,4,2) के रूप में संग्रहीत किया जाएगा।

डी सबलिस्ट में एक ही संख्या के 2 या अधिक दोहराव होते हैं।

मेरे 3 सबलिस्ट हेडर मान "ए", "बी" और "सी" होंगे, इसलिए मुझे डी-टाइप सबस्टलिस्ट का प्रतिनिधित्व करने का एक तरीका चाहिए।

मान लीजिए मेरे पास सी-टाइप सबलिस्ट शीर्षक है, जिसके बाद 3 प्रविष्टियाँ हैं, जैसे कि "C [17] [101] [58]"। यह ऊपर वर्णित के रूप में मान्य सी-टाइप सबलिस्ट का हिस्सा नहीं हो सकता है, क्योंकि तीसरी प्रविष्टि दूसरी से कम है लेकिन पहली से अधिक है। मैं इस प्रकार के निर्माण का उपयोग डी-टाइप सबलिस्ट को दर्शाने के लिए कर सकता हूं। थोड़े शब्दों में, कहीं भी मेरे पास "C {00 ?????}} {1 ?????}} {01 ?????}" एक असंभव सी-टाइप सबलिस्ट है। मैं इसका उपयोग किसी एकल संख्या के 3 या अधिक दोहराव वाले सबलिस्ट को दर्शाने के लिए करूंगा। पहले दो 7-बिट शब्द संख्या को (नीचे "एन" बिट्स) को एन्कोड करते हैं और उसके बाद शून्य या अधिक {0100001} शब्दों के बाद {0100000} शब्द आते हैं।

For example, 3 repetitions: "C{00NNNNN}{1NN0000}{0100000}", 4 repetitions: "C{00NNNNN}{1NN0000}{0100001}{0100000}", and so on.

यह सिर्फ उन सूचियों को छोड़ देता है जो एक ही संख्या के 2 दोहराव रखती हैं। मैं एक और असंभव सी-टाइप सबलिस्ट पैटर्न के साथ उन लोगों का प्रतिनिधित्व करता हूं: "C {0 ??????} {11 ?????} {10 ?????}"। पहले 2 शब्दों में संख्या के 7 बिट्स के लिए बहुत जगह है, लेकिन यह पैटर्न उस सबलिस्ट से अधिक लंबा है जो यह प्रतिनिधित्व करता है, जो चीजों को थोड़ा अधिक जटिल बनाता है। अंत में पांच प्रश्न-चिह्नों को पैटर्न का हिस्सा नहीं माना जा सकता है, इसलिए मेरे पास: "C {0NNNNNN} {11N ????} 10" मेरे पैटर्न के रूप में, जिस संख्या को "N" में दोहराया जाना है। "है। वह भी 2 बिट्स लंबा।

मुझे 2 बिट्स उधार लेने होंगे और उन्हें इस पैटर्न में 4 अप्रयुक्त बिट्स से वापस भुगतान करना होगा। पढ़ते समय, "C {0NNNNNN} {11N00AB} 10", एन "s" में संख्या के आउटपुट 2 इंस्टेंस, बिट्स A और B के साथ अंत में "10" को ओवरराइट करें, और 2 से रीड पॉइंटर को रिवाइंड करें। बिट्स। इस एल्गोरिथ्म के लिए विनाशकारी रीड्स ठीक हैं, क्योंकि प्रत्येक कॉम्पैक्ट सूची केवल एक बार चली जाती है।

एक एकल संख्या के 2 दोहराव का एक सबलिस्ट लिखने के दौरान, "C {0NNNNN} 11N00" लिखें और उधार बिट्स काउंटर को 2 पर सेट करें। प्रत्येक लेखन में जहां उधार बिट्स काउंटर गैर-शून्य है, यह प्रत्येक बिट लिखित के लिए अस्वीकृत है और "10" तब लिखा जाता है जब काउंटर शून्य से टकराता है। तो अगले 2 बिट लिखे गए स्लॉट ए और बी में जाएंगे, और फिर "10" अंत में छोड़ दिया जाएगा।

"00", "01" और "1" द्वारा दर्शाए गए 3 सबलिस्ट हेडर मूल्यों के साथ, मैं "1" को सबसे लोकप्रियलिस्ट प्रकार में असाइन कर सकता हूं। मुझे सबलिस्ट प्रकारों को सबलिस्ट प्रकारों को मैप करने के लिए एक छोटी तालिका की आवश्यकता होगी, और मुझे प्रत्येक सबलिस्ट प्रकार के लिए एक घटना काउंटर की आवश्यकता होगी ताकि मुझे पता चले कि सबसे अच्छा सबलिस्ट हेडर मैपिंग क्या है।

पूरी तरह से आबादी वाली कॉम्पैक्ट सूची में सबसे खराब स्थिति न्यूनतम प्रतिनिधित्व तब होती है जब सभी सबलिस्ट प्रकार समान रूप से लोकप्रिय होते हैं। उस स्थिति में मैं प्रत्येक 3 सबलिस्ट हेडर के लिए 1 बिट बचाता हूं, इसलिए सूची का आकार 2 * 781250 + 7 * 1000000 - 781250/3 = 8302083.3 बिट्स है। 32 बिट शब्द सीमा तक राउंडिंग, 8302112 बिट्स, या 1037764 बाइट्स।

टीसीपी / आईपी राज्य के लिए 1 एम माइनस 2k और बफ़र्स 1022 * 1024 = 1046528 बाइट्स हैं, जिससे मुझे खेलने के लिए 8764 बाइट्स मिलेंगे।

लेकिन सबलिस्ट हेडर मैपिंग को बदलने की प्रक्रिया के बारे में क्या? नीचे दिए गए स्मृति मानचित्र में, "Z" यादृच्छिक ओवरहेड है, "=" मुक्त स्थान है, "X" कॉम्पैक्ट सूची है।

ZZZ=====XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

बाईं ओर "X" पढ़ना शुरू करें और बाईं ओर "=" लिखना शुरू करें और दाएं काम करें। जब यह किया जाता है तो कॉम्पैक्ट सूची थोड़ी छोटी हो जाएगी और यह मेमोरी के गलत छोर पर होगा:

ZZZXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=======

तो मैं इसे सही करने के लिए अलग करने की आवश्यकता होगी:

ZZZ=======XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

हेडर मैपिंग परिवर्तन प्रक्रिया में, सबलिस्ट हेडर के 1/3 तक 1-बिट से 2-बिट में बदल जाएगा। सबसे खराब स्थिति में ये सभी सूची के प्रमुख होंगे, इसलिए मुझे शुरू करने से पहले कम से कम 781250/3 बिट्स मुफ्त भंडारण की आवश्यकता होगी, जो मुझे कॉम्पैक्ट सूची के पिछले संस्करण की स्मृति आवश्यकताओं पर वापस ले जाता है: (

उस के आसपास जाने के लिए, मैं 7812 सब्लिस्ट्स को 78125 सबलिस्ट्स के 10 सबलिस्ट समूहों में विभाजित करूँगा। प्रत्येक समूह का अपना स्वतंत्र सबलिस्ट हेडर मैपिंग होता है। समूहों के लिए A से J अक्षर का उपयोग करना:

ZZZ=====AAAAAABBCCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ

प्रत्येक सबलिस्ट समूह सिकुड़ता हैडर मैपिंग परिवर्तन के दौरान सिकुड़ता या ठहरता है:

ZZZ=====AAAAAABBCCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAA=====BBCCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABB=====CCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCC======DDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDD======EEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEE======FFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFF======GGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGG=======HHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHH=======IJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHHI=======JJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ=======
ZZZ=======AAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ

मैपिंग परिवर्तन के दौरान एक सबलिस्ट समूह का सबसे खराब मामला 4k के तहत 78125/3 = 26042 बिट्स है। अगर मैं पूरी तरह से आबादी वाली कॉम्पैक्ट सूची के लिए 4k प्लस 1037764 बाइट्स की अनुमति देता हूं, तो मुझे स्मृति मानचित्र में "जेड" के लिए 8764 - 4096 = 4668 बाइट्स छोड़ देता है।

10 सबलिस्ट हेडर मैपिंग टेबल, 30 सबलिस्ट हेडर घटना काउंट और अन्य कुछ काउंटर्स, पॉइंटर्स और छोटे बफ़र्स जिनकी मुझे आवश्यकता होगी, और स्पेस मैंने नोट किए बिना उपयोग किया है, जैसे फंक्शन कॉल रिटर्न एड्रेस के लिए स्टैक स्पेस और स्थानीय चर।

भाग 3, इसे चलाने में कितना समय लगेगा?

खाली कॉम्पैक्ट सूची के साथ 1-बिट सूची शीर्ष लेख का उपयोग रिक्त सबलिस्ट के लिए किया जाएगा, और सूची का प्रारंभिक आकार 78,505 बिट्स होगा। सबसे खराब स्थिति में सूची में जोड़े गए प्रत्येक नंबर के लिए 8 बिट्स बढ़ते हैं, इसलिए 32-बिट संख्याओं में से प्रत्येक के लिए 32 + 8 = 40 बिट्स रिक्त स्थान की आवश्यकता होती है जिसे सूची बफर के शीर्ष पर रखा जाता है और फिर सॉर्ट और मर्ज किया जाता है। सबसे खराब स्थिति में, 2 * 781250 + 7 * प्रविष्टियों के अंतरिक्ष उपयोग में सबलिस्ट हेडर मैपिंग को बदलना - 781250/3 बिट्स।

सूची में कम से कम 800000 नंबर होने के बाद प्रत्येक पाँचवें मर्ज के बाद सबलिस्ट हेडर मैपिंग को बदलने की नीति के साथ, एक सबसे खराब स्थिति में कुल 30M कॉम्पैक्ट सूची पढ़ने और लिखने की गतिविधि शामिल होगी।

स्रोत:

http://nick.cleaton.net/ramsortsol.html


15
मुझे नहीं लगता कि कोई भी बेहतर समाधान संभव है (यदि हमें किसी अचूक मूल्यों के साथ काम करने की आवश्यकता है)। लेकिन यह एक छोटा सुधार हो सकता है। 1-बिट और 2-बिट अभ्यावेदन के बीच सबलिस्ट हेडर को बदलना आवश्यक नहीं है। इसके बजाय आप अंकगणित कोडिंग का उपयोग कर सकते हैं , जो एल्गोरिथ्म को सरल करता है और 1.67 से 1.58 तक प्रति हेडर में सबसे खराब-केस संख्या भी घट जाती है। और आपको मेमोरी में कॉम्पैक्ट सूची को स्थानांतरित करने की आवश्यकता नहीं है; इसके बजाय परिपत्र बफर का उपयोग करें और केवल संकेत बदलें।
बजे एवगेनी क्लूव

5
तो, आखिरकार, वह एक साक्षात्कार प्रश्न था?
mlvjjr

2
अन्य संभावित सुधार 128-तत्व सब्लिस्ट्स के बजाय 100-तत्व सब्लिस्ट्स का उपयोग करना है (क्योंकि हम सबसे कॉम्पैक्ट प्रतिनिधित्व प्राप्त करते हैं जब सबलेस्ट्स की संख्या डेटा सेट में तत्वों की संख्या के बराबर होती है)। सबलिस्ट के प्रत्येक मूल्य को अंकगणित कोडिंग (प्रत्येक मूल्य के लिए 1/100 की समान आवृत्ति के साथ) से एन्कोड किया जाना है। यह लगभग 10000 बिट्स को बचा सकता है, सबलिस्ट हेडर के संपीड़न से बहुत कम।
एवगेनी क्लूव

केस C के लिए, आप कहते हैं "प्रविष्टियां गैर-घटते क्रम में संग्रहीत की जाती हैं, सिवाय इसके कि अंतिम प्रविष्टि पहले की तुलना में कम या बराबर है।" फिर आप 2,2,2,3,5 को कैसे एनकोड करेंगे? {2,2,3,5,2} बस 2,2 कैसा लगेगा
Rollie

1
सबलिस्ट हेडर एन्कोडिंग का एक सरल समाधान एक ही संपीड़न अनुपात के साथ संभव है। 1.67 बिट्स प्रति सबडिडर को बिना जटिल स्विचिंग मैपिंग के। आप हर 3 लगातार उपशाखाओं को एक साथ जोड़ सकते हैं, जो 5 बिट्स में आसान एनकोडेड हो सकता है 3 * 3 * 3 = 27 < 32। आप उन्हें संयोजित करें combined_subheader = subheader1 + 3 * subheader2 + 9 * subheader3
हाइनेकर

57

इसकी धारणाओं में गिलमनोव का जवाब बहुत गलत है। यह एक मिलियन लगातार पूर्णांक के निरर्थक माप में आधारित अनुमान लगाना शुरू करता है । इसका मतलब कोई अंतराल नहीं है। वे यादृच्छिक अंतराल, हालांकि छोटे, वास्तव में इसे एक खराब विचार बनाते हैं।

इसे स्वयं आज़माएं। 1 मिलियन यादृच्छिक 27 बिट पूर्णांक प्राप्त करें, उन्हें सॉर्ट करें, 7-ज़िप , xz के साथ संपीड़ित करें , जो भी LZMA आप चाहते हैं। परिणाम 1.5 एमबी से अधिक है। शीर्ष पर आधार अनुक्रमिक संख्याओं का संपीड़न है। यहां तक ​​कि डेल्टा एन्कोडिंग 1.1 एमबी से अधिक है । और यह कभी नहीं लगता कि यह संपीड़न के लिए 100 एमबी रैम का उपयोग कर रहा है। तो यहां तक ​​कि संपीड़ित पूर्णांक समस्या के लायक नहीं हैं और कभी भी समय रैम उपयोग को ध्यान में न रखें

यह मुझे दुखद है कि कैसे लोग बस सुंदर ग्राफिक्स और युक्तिकरण को बढ़ाते हैं।

#include <stdint.h>
#include <stdlib.h>
#include <time.h>

int32_t ints[1000000]; // Random 27-bit integers

int cmpi32(const void *a, const void *b) {
    return ( *(int32_t *)a - *(int32_t *)b );
}

int main() {
    int32_t *pi = ints; // Pointer to input ints (REPLACE W/ read from net)

    // Fill pseudo-random integers of 27 bits
    srand(time(NULL));
    for (int i = 0; i < 1000000; i++)
        ints[i] = rand() & ((1<<27) - 1); // Random 32 bits masked to 27 bits

    qsort(ints, 1000000, sizeof (ints[0]), cmpi32); // Sort 1000000 int32s

    // Now delta encode, optional, store differences to previous int
    for (int i = 1, prev = ints[0]; i < 1000000; i++) {
        ints[i] -= prev;
        prev    += ints[i];
    }

    FILE *f = fopen("ints.bin", "w");
    fwrite(ints, 4, 1000000, f);
    fclose(f);
    exit(0);

}

अब LZMA के साथ ints.bin को संपीड़ित करें ...

$ xz -f --keep ints.bin       # 100 MB RAM
$ 7z a ints.bin.7z ints.bin   # 130 MB RAM
$ ls -lh ints.bin*
    3.8M ints.bin
    1.1M ints.bin.7z
    1.2M ints.bin.xz

7
डिक्शनरी बेस्ड कम्प्रेशन से युक्त कोई भी एल्गोरिथ्म मंदबुद्धि से परे है, मैंने कुछ कस्टम को कोडित किया है और वे सभी अपनी हैश टेबलों को लगाने के लिए काफी मेमोरी लेते हैं (और जावा में कोई हाशपॅप नहीं है क्योंकि यह संसाधनों पर अतिरिक्त भूख है)। निकटतम समाधान डेल्टा एन्कोडिंग डब्ल्यू / चर बिट लंबाई और उछल वापस टीसीपी पैकेट आपको पसंद नहीं होगा। सहकर्मी प्रतिशोध करेगा, फिर भी सबसे अच्छा है।
२३

@bestsss हाँ! मेरे अंतिम प्रगति उत्तर की जाँच करें। मुझे लगता है कि यह संभव हो सकता है।
एलेकियो

3
क्षमा करें, लेकिन यह वास्तव में प्रश्न का उत्तर नहीं देता है ।
n611x007

@naxa हाँ, इसका उत्तर है: यह मूल प्रश्न के मापदंडों के भीतर नहीं किया जा सकता है। यह केवल तभी किया जा सकता है जब संख्याओं के वितरण में बहुत कम एन्ट्रापी हो।
alecco

1
यह सब उत्तर दिखाता है कि मानक संपीड़न दिनचर्या में 1MB से नीचे के डेटा को संपीड़ित करने में कठिनाई होती है। ऐसी एन्कोडिंग योजना नहीं हो सकती है जो 1MB से कम की आवश्यकता के लिए डेटा को संपीड़ित कर सकती है, लेकिन यह उत्तर साबित नहीं करता है कि कोई एन्कोडिंग योजना नहीं है जो डेटा को बहुत कम कर देगी।
इसकाme2003

41

मुझे लगता है कि इस बारे में सोचने का एक तरीका जुझारू दृष्टिकोण से है: क्रमबद्ध संख्या क्रमों के कितने संभव संयोजन हैं? यदि हम संयोजन 0,0,0, ...., 0 कोड 0, और 0,0,0, ..., 1 कोड 1, और 99999999, 99999999, ... 99999999 कोड N, N क्या है? दूसरे शब्दों में, परिणाम स्थान कितना बड़ा है?

वैसे, इस बारे में सोचने का एक तरीका यह है कि यह एक एन एक्स एम ग्रिड, जहां एन = 1,000,000 और एम = 100,000,000 में मोनोटोनिक पथों की संख्या खोजने की समस्या का एक आक्षेप है। दूसरे शब्दों में, यदि आपके पास एक ग्रिड है जो 1,000,000 चौड़ा और 100,000,000 लंबा है, तो नीचे दाएं से बाएं शीर्ष तक कितने छोटे रास्ते हैं? निश्चित रूप से सबसे छोटे रास्तों के लिए आपको कभी-कभी ही सही या आगे बढ़ना पड़ता है (यदि आप नीचे जाने के लिए गए या बाएं गए तो आप पहले की गई प्रगति को पूर्ववत कर देंगे)। यह देखने के लिए कि यह हमारी संख्या छँटाई समस्या की एक आपत्ति कैसे है, निम्नलिखित देखें:

आप हमारे आदेश में एक संख्या के रूप में हमारे रास्ते में किसी भी क्षैतिज पैर की कल्पना कर सकते हैं, जहां पैर का वाई स्थान मूल्य का प्रतिनिधित्व करता है।

यहां छवि विवरण दर्ज करें

इसलिए यदि पथ केवल अंत तक सभी तरह से दाईं ओर जाता है, तो शीर्ष पर सभी तरह से कूदता है, जो कि ऑर्डर करने के लिए 0,0,0, ..., 0 के बराबर है। यदि यह इसके बजाय शीर्ष पर सभी तरह से कूदना शुरू करता है और फिर सही 1,000,000 बार चलता है, तो यह 99999999,99999999, ... 99999999 के बराबर है। एक ऐसा रास्ता जहाँ यह एक बार सही तरीके से चलता है, फिर एक बार ऊपर जाता है, फिर एक सही , तो एक बार आदि, बहुत अंत तक (फिर आवश्यक रूप से शीर्ष पर सभी तरह से कूदता है), 0,1,2,3, ..., 999999 के बराबर है।

सौभाग्य से हमारे लिए यह समस्या पहले ही हल हो गई है, इस तरह के एक ग्रिड (एन + एम) चुनें (एम) पथ:

(1,000,000 + 100,000,000) चुनें (100,000,000) ~ = 2.27 * 10 ^ 2436455

एन इस प्रकार 2.27 * 10 ^ 2436455 के बराबर होता है, और इसलिए कोड 0 0,0,0, ..., 0 और कोड 2.27 * 10 ^ 2436455 का प्रतिनिधित्व करता है और कुछ परिवर्तन 99999999,99999999, ..., 99999999 का प्रतिनिधित्व करता है।

सभी नंबरों को 0 से 2.27 * 10 ^ 2436455 तक स्टोर करने के लिए आपको lg2 (2.27 * 10 ^ 2436455) = 8.0937 * 10 ^ 6 बिट्स की आवश्यकता होगी।

1 मेगाबाइट = 8388608 बिट्स> 8093700 बिट्स

तो ऐसा प्रतीत होता है कि परिणाम को संग्रहीत करने के लिए हमारे पास कम से कम वास्तव में पर्याप्त जगह है! अब निश्चित रूप से दिलचस्प बिट क्रम संख्या के रूप में छँटाई कर रहा है। यकीन नहीं कि इसके लिए सबसे अच्छा तरीका दिया गया है हमारे पास 294908 बिट्स शेष हैं। मुझे लगता है कि प्रत्येक बिंदु पर एक दिलचस्प तकनीक होगी जो यह मानती है कि वह संपूर्ण ऑर्डर है, उस ऑर्डर के लिए कोड ढूंढ रहा है, और फिर जैसा कि आप एक नया नंबर प्राप्त करते हैं और पिछले कोड को अपडेट करते हैं। हाथ तरंग हाथ लहर।


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

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

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

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

"मूल सवाल पूछने वाले के जवाब से <- आदेश की डुप्लिकेट अनुमति और आदेश के साथ 1 मिलियन 8-अंकीय संख्या चुनने के लिए विभिन्न तरीकों से लगभग 2 से 8093729.5 अलग-अलग तरीके हैं" <-। मुझे नहीं पता कि मैं किस बारे में बात कर रहा हूं। मैंने अपनी अंतिम टिप्पणी में इस वाक्य का विशेष रूप से उल्लेख किया है।
डैनियल वैगनर

20

मेरे सुझाव यहाँ दान के समाधान के लिए बहुत कुछ है

सबसे पहले मैं मानता हूं कि समाधान को सभी संभावित इनपुट सूचियों को संभालना चाहिए । मुझे लगता है कि लोकप्रिय उत्तर यह धारणा नहीं बनाते हैं (जो IMO एक बहुत बड़ी गलती है)।

यह ज्ञात है कि दोषरहित संपीड़न का कोई भी रूप सभी इनपुट के आकार को कम नहीं करेगा।

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

इस तरह के एक समाधान के लिए, कोई भी व्यक्ति जो यह जानता है कि वे अपनी संपीड़न कैसे करते हैं, कुछ इनपुट डेटा को डिजाइन करने में सक्षम होगा जो इस योजना के लिए अच्छी तरह से संपीड़ित नहीं करता है, और "समाधान" सबसे अधिक संभावना है कि अंतरिक्ष से बाहर भागने के कारण टूट जाएगा।

इसके बजाय मैं गणितीय दृष्टिकोण अपनाता हूं। हमारे संभावित आउटपुट लंबाई की सभी लिस्ट हैं। रेंज में तत्वों में LEN 0.AX शामिल हैं। यहाँ LEN 1,000,000 है और हमारा MAX 100,000,000 है।

LEN और MAX की मनमानी के लिए, इस राज्य को एन्कोड करने के लिए आवश्यक बिट्स की राशि है:

लॉग 2 (मैक्स मल्टीचोज़ लेन)

इसलिए हमारी संख्याओं के लिए, एक बार जब हमने पुनरावृत्ति और छँटाई पूरी कर ली है, तो हमें अपने परिणाम को स्टोर करने के लिए कम से कम Log2 (100,000,000 MC 1,000,000) बिट्स की आवश्यकता होगी जो विशिष्ट रूप से सभी संभावित आउटपुट को अलग कर सकते हैं।

यह ~ = 988kb है । इसलिए हमारे पास वास्तव में हमारे परिणाम को रखने के लिए पर्याप्त जगह है। इस दृष्टि से यह संभव है।

[हटाए गए व्यर्थ के जुमले अब बेहतर उदाहरण हैं ...]

सबसे अच्छा जवाब यहाँ है

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


हमेशा अगले दिन अपने स्वयं के उत्तर को फिर से पढ़ने के लिए मज़ेदार ... इसलिए जब तक कि शीर्ष उत्तर गलत है, स्वीकार किए जाते हैं एक stackoverflow.com/a/12978097/1763801 बहुत अच्छा है। मूल रूप से प्रविष्टि LEN-1 को लेने और LEN को वापस करने के लिए फ़ंक्शन के रूप में सम्मिलन प्रकार का उपयोग करता है। इस तथ्य को कैपिटल करता है कि यदि आप एक छोटे सेट को निर्धारित करते हैं तो आप दक्षता बढ़ाने के लिए उन सभी को एक पास में डाल सकते हैं। राज्य का प्रतिनिधित्व मेरे हाथ से लहराते सुझाव और अधिक सहज ज्ञान से बेहतर कॉम्पैक्ट (7 बिट संख्या की बाल्टी) है। अपने कंप्यूटर अनुप्रयोग भू विचार bollocks, क्षमा करें थे
davec

1
मुझे लगता है कि आपका अंकगणित थोड़ा हटकर है। मैं lg2 (100999999! / - (99999999! * 1000000!)) = 1011718.55
NovaDenizen

हाँ धन्यवाद यह 988kb था 965 नहीं। मैं 1024 बनाम 1000 के मामले में सुस्त था। हम अभी भी लगभग 35kb के साथ खेलना बाकी है। मैंने उत्तर में गणित की गणना के लिए एक लिंक जोड़ा।
17

18

मान लीजिए यह कार्य संभव है। आउटपुट से ठीक पहले, मिलियन सॉर्ट किए गए नंबरों का इन-मेमोरी प्रतिनिधित्व होगा। ऐसे कितने निरूपण हैं? चूंकि बार-बार नंबर हो सकते हैं हम nCr (चयन) का उपयोग नहीं कर सकते हैं, लेकिन मल्टीचोज़ नामक एक ऑपरेशन है जो मल्टीसेट पर काम करता है ।

  • हैं 2.2e2436455 रेंज 0..99,999,999 में एक लाख संख्या का चयन करने के तरीके।
  • हर संभावित संयोजन या 1,011,717 बाइट का प्रतिनिधित्व करने के लिए 8,093,730 बिट्स की आवश्यकता होती है ।

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

हालांकि, अगर "1M रैम" का मतलब एक मिलियन बाइट्स है, तो स्पष्ट रूप से पर्याप्त जगह नहीं है। यह तथ्य कि 5% अधिक मेमोरी इसे सैद्धांतिक रूप से संभव बनाती है, मुझे यह बताती है कि प्रतिनिधित्व को बहुत कुशल और शायद समझदार नहीं होना चाहिए।


एक लाख संख्या (2.2e2436455) चुनने के तरीकों की संख्या (256 ^ (1024 * 988)) के करीब होती है, जो (2.0e2436445) है। एर्गो, यदि आप 1M से लगभग 32 KB मेमोरी लेते हैं, तो समस्या हल नहीं हो सकती। यह भी ध्यान रखें कि कम से कम 3 KB मेमोरी आरक्षित थी।
जॉन्हबर्ड

यह निश्चित रूप से मानता है कि डेटा पूरी तरह से यादृच्छिक है। जहां तक ​​हम जानते हैं, यह है, लेकिन मैं अभी कह रहा हूं :)
थोरारिन

संभावित राज्यों की इस संख्या का प्रतिनिधित्व करने का पारंपरिक तरीका लॉग बेस 2 लेकर और उन्हें प्रतिनिधित्व करने के लिए आवश्यक बिट्स की संख्या की रिपोर्ट करना है।
नोवाडेनिज़ेन

@Thorarin, हाँ, मैं एक "समाधान" में कोई मतलब नहीं है कि केवल कुछ जानकारी के लिए काम करता है।
डैन

12

(मेरा मूल उत्तर गलत था, खराब गणित के लिए खेद है, ब्रेक के नीचे देखें।)

इस बारे में कैसा है?

पहले 27 बिट्स आपके द्वारा देखे गए सबसे कम संख्या को स्टोर करते हैं, फिर अगले नंबर पर देखा गया अंतर, निम्नानुसार एन्कोडेड: अंतर को संग्रहीत करने में उपयोग किए जाने वाले बिट्स की संख्या को स्टोर करने के लिए 5 बिट्स, फिर अंतर। यह इंगित करने के लिए कि आपने उस नंबर को फिर से देखा है, 00000 का उपयोग करें।

यह काम करता है क्योंकि जैसे ही अधिक संख्याएं डाली जाती हैं, संख्याओं के बीच औसत अंतर कम हो जाता है, इसलिए आप अंतर को संग्रहीत करने के लिए कम बिट का उपयोग करते हैं क्योंकि आप अधिक संख्या जोड़ते हैं। मेरा मानना ​​है कि इसे डेल्टा सूची कहा जाता है।

सबसे खराब स्थिति मैं सोच सकता हूँ कि सभी संख्याएँ समान रूप से (100 से कम) हैं, उदाहरण के लिए मान लें कि पहली संख्या है:

000000000000000000000000000 00111 1100100
                            ^^^^^^^^^^^^^
                            a million times

27 + 1,000,000 * (5+7) bits = ~ 427k

बचाव के लिए रेडिट!

यदि आप सभी को उन्हें सुलझाना था, तो यह समस्या आसान होगी। यह स्टोर करने के लिए 122k (1 मिलियन बिट्स) लेता है जो आपने देखा है (0 बिट पर अगर 0 देखा गया था, 2300 बिट पर अगर 2300 देखा गया था, आदि।

आप संख्याओं को पढ़ते हैं, उन्हें बिट फ़ील्ड में संग्रहीत करते हैं, और फिर एक गिनती रखते हुए बिट्स को बाहर स्थानांतरित करते हैं।

लेकिन, आपको यह याद रखना होगा कि आपने कितने देखे हैं। मैं इस योजना के साथ आने के लिए ऊपर दिए गए उत्तर सूची से प्रेरित था:

एक बिट का उपयोग करने के बजाय, 2 या 27 बिट्स का उपयोग करें:

  • 00 का मतलब है कि आपने नंबर नहीं देखा है।
  • 01 का मतलब है कि आपने इसे एक बार देखा था
  • 1 का मतलब है कि आपने इसे देखा था, और अगले 26 बिट्स कितनी बार की गिनती हैं।

मुझे लगता है कि यह काम करता है: यदि कोई डुप्लिकेट नहीं हैं, तो आपके पास 244k सूची है। सबसे खराब स्थिति में आप प्रत्येक नंबर को दो बार देखते हैं (यदि आप एक नंबर को तीन बार देखते हैं, तो यह आपके लिए बाकी सूची को छोटा करता है), इसका मतलब है कि आपने 50,000 से अधिक बार देखा है, और आपने 950,000 आइटम 0 या 1 बार देखे हैं।

50,000 * 27 + 950,000 * 2 = 396.7k।

यदि आप निम्नलिखित एन्कोडिंग का उपयोग करते हैं तो आप और सुधार कर सकते हैं:

0 का मतलब है कि आपने संख्या 10 नहीं देखी है, इसका मतलब है कि आपने इसे 11 बार देखा है कि आप गिनती कैसे करते हैं

जो औसतन, 280.7k स्टोरेज का परिणाम देगा।

संपादित करें: मेरा रविवार की सुबह गणित गलत था।

सबसे खराब स्थिति यह है कि हम 500,000 संख्याओं को दो बार देखते हैं, इसलिए गणित बन जाता है:

500,000 * 27 + 500,000 * 2 = 1.77M

के एक औसत भंडारण में वैकल्पिक एन्कोडिंग परिणाम है

500,000 * 27 + 500,000 = 1.70M

: (


1
ठीक है, नहीं, चूंकि दूसरा नंबर 500000 होगा।
जफरानंद

हो सकता है कि कुछ इंटरमीडिएट जोड़ें, जैसे जहां 11 का मतलब है कि आपने 64 बार (अगले 6 बिट्स का उपयोग करके) नंबर देखा है, और 11000000 का मतलब है कि आपने जो बार देखा है उसकी संख्या को स्टोर करने के लिए अन्य 32 बिट्स का उपयोग करें।
τεκ

10
आपको "1 मिलियन बिट्स" संख्या कहां से मिली? आपने कहा कि 2300 का प्रतिनिधित्व करता है कि क्या 2300 को देखा गया था। (मुझे लगता है कि आपको वास्तव में 2301 वां मतलब था।) कौन सा बिट प्रतिनिधित्व करता है कि क्या 99,999,999 को देखा गया था (8-अंकों की सबसे बड़ी संख्या)? संभवतः, यह 100-मिलियन बिट होगा।
15:

आपको अपना एक मिलियन और आपका सौ मिलियन पीछे की तरफ मिला। सबसे अधिक बार एक मूल्य हो सकता है 1 मिलियन, और एक मूल्य की घटनाओं की संख्या का प्रतिनिधित्व करने के लिए आपको केवल 20 बिट्स की आवश्यकता होती है। इसी तरह आपको 100,000,000 बिट फ़ील्ड (1 मिलियन नहीं) की आवश्यकता है, प्रत्येक संभावित मूल्य के लिए एक।
टिम आर।

उह, 27 + 1000000 * (5 + 7) = 12000027 बिट्स = 1.43 एम, 427K नहीं।
डैनियल वैगनर

10

सभी संभावित आदानों में इस समस्या का एक समाधान है। धोखा।

  1. टीसीपी पर एम मान पढ़ें, जहां एम अधिकतम के पास है जिसे मेमोरी में सॉर्ट किया जा सकता है, शायद n / 4।
  2. २५०,००० (या तो) क्रमबद्ध करें और उन्हें आउटपुट करें।
  3. अन्य 3 तिमाहियों के लिए दोहराएं।
  4. रिसीवर को उन संख्याओं की 4 सूचियों को मर्ज करने दें जो उन्हें प्राप्त हुई हैं क्योंकि यह उन्हें संसाधित करता है। (यह एक सूची का उपयोग करने की तुलना में बहुत धीमी नहीं है।)

7

मैं एक मूलांक ट्री की कोशिश करूंगा । यदि आप डेटा को ट्री में स्टोर कर सकते हैं, तो आप डेटा ट्रांसमिट करने के लिए एक इन-ऑर्डर ट्रैवर्स कर सकते हैं।

मुझे यकीन नहीं है कि आप इसे 1MB में फिट कर सकते हैं, लेकिन मुझे लगता है कि यह एक कोशिश के लायक है।


7

आप किस तरह का कंप्यूटर इस्तेमाल कर रहे हैं? इसके पास कोई अन्य "सामान्य" स्थानीय भंडारण नहीं हो सकता है, लेकिन क्या इसके पास वीडियो रैम है, उदाहरण के लिए? 1 मेगापिक्सेल x 32 बिट प्रति पिक्सेल (कहना) आपके आवश्यक डेटा इनपुट आकार के बहुत करीब है।

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


1
टिप्पणी करने के लिए परवाह, downvoter? - मैं सिर्फ सवाल के स्पष्ट विरोधाभासों को खींचने की कोशिश कर रहा हूं (अर्थात रचनात्मक रूप से धोखा ;-)
डीएनए

हो सकता है कि कोई कंप्यूटर न हो, क्योंकि हैकर समाचार पर संबंधित सूत्र में यह उल्लेख है कि एक बार Google साक्षात्कार प्रश्न था।
20

1
हाँ - मैंने उत्तर दिया कि प्रश्न को संपादित करने से पहले संकेत दिया गया था कि यह एक साक्षात्कार प्रश्न है!
डीएनए

6

मूलांक वृक्ष का प्रतिनिधित्व इस समस्या से निपटने के करीब आएगा, क्योंकि मूलांक वृक्ष "उपसर्ग संपीड़न" का लाभ उठाता है। लेकिन एक मूलांक वृक्ष के प्रतिनिधित्व की कल्पना करना कठिन है जो एक बाइट में एक नोड का प्रतिनिधित्व कर सकता है - दो शायद सीमा के बारे में है।

लेकिन, इस बात की परवाह किए बिना कि डेटा का प्रतिनिधित्व कैसे किया जाता है, एक बार छंटनी के बाद इसे उपसर्ग-संपीड़ित रूप में संग्रहीत किया जा सकता है, जहां संख्या 10, 11 और 12 को दर्शाया जाएगा, 001b, 001b, 001b को 1 के वेतन वृद्धि का संकेत देता है। पिछली संख्या से। शायद, तब, १०१०१ बी ५, ११०१००१ ख में ९ की वेतन वृद्धि आदि का प्रतिनिधित्व करेगा।


6

10 ^ 8 की श्रेणी में 10 ^ 6 मान हैं, इसलिए औसतन प्रति सौ कोड बिंदुओं पर एक मूल्य है। Nth बिंदु से (N + 1) वें की दूरी को स्टोर करें। डुप्लिकेट मानों की एक स्किप है 0. इसका मतलब है कि स्किप को स्टोर करने के लिए सिर्फ 7 बिट्स के तहत औसत की आवश्यकता है, इसलिए उनमें से एक लाख खुशी से हमारे 8 मिलियन बिट्स स्टोरेज में फिट हो जाएगा।

हफ़मैन एन्कोडिंग द्वारा कहा गया है कि इन झालरों को एक बिटस्ट्रीम में एन्कोड किया जाना चाहिए। सम्मिलन बिटस्ट्रीम के माध्यम से पुनरावृत्ति करके और नए मूल्य के बाद पुन: लिखना है। निहित मूल्यों के माध्यम से पुनरावृति और लेखन द्वारा आउटपुट। व्यावहारिकता के लिए, यह संभवतः के रूप में किया जाना चाहता है, कहते हैं, 10 ^ 4 कोड बिंदुओं को कवर करने वाली 10 ^ 4 सूचियाँ (और औसतन 100 मान)।

यादृच्छिक डेटा के लिए एक अच्छा हफ़मैन पेड़ को पिप्स के वितरण पर एक पॉइज़न वितरण (मतलब = विचरण = 100) मानकर एक प्राथमिकता का निर्माण किया जा सकता है, लेकिन वास्तविक आँकड़ों को इनपुट पर रखा जा सकता है और इससे निपटने के लिए एक इष्टतम पेड़ उत्पन्न किया जा सकता है। रोग संबंधी मामले।


5

मेरे पास 1M RAM वाला कंप्यूटर है और कोई अन्य स्थानीय भंडारण नहीं है

धोखा देने का एक और तरीका: आप इसके बजाय गैर-स्थानीय (नेटवर्क) स्टोरेज का उपयोग कर सकते हैं (आपका प्रश्न इस बात को रोकता नहीं है) और एक नेटवर्क सेवा को कॉल करें, जो सीधी डिस्क-आधारित मर्जोर्ट (या मेमोरी में सॉर्ट करने के लिए केवल पर्याप्त रैम का उपयोग कर सकती है) केवल (केवल अत्यंत सरल) समाधान की आवश्यकता के बिना, 1M संख्या स्वीकार करने की आवश्यकता है।

यह धोखा हो सकता है, लेकिन यह स्पष्ट नहीं है कि क्या आप वास्तविक दुनिया की समस्या का हल ढूंढ रहे हैं, या एक पहेली जो नियमों के झुकने को आमंत्रित करती है ... यदि बाद वाला, तो एक साधारण धोखा एक जटिल से बेहतर परिणाम प्राप्त कर सकता है। लेकिन "वास्तविक" समाधान (जैसा कि दूसरों ने बताया है, केवल संकुचित इनपुट के लिए काम कर सकता है)।


5

मुझे लगता है कि समाधान वीडियो एन्कोडिंग से तकनीकों को संयोजित करना है, अर्थात् असतत कोसाइन परिवर्तन। डिजिटल वीडियो में, 110 112 115 116 जैसे नियमित मूल्यों के रूप में वीडियो की चमक या रंग को बदलते हुए, प्रत्येक को अंतिम (लंबाई एन्कोडिंग चलाने के समान) से घटाया जाता है। 110 112 115 116 110 2 3 1 हो जाता है। मूल्यों, 2 3 1 को मूल से कम बिट्स की आवश्यकता होती है।

तो हम कहते हैं कि हम इनपुट मानों की एक सूची बनाते हैं क्योंकि वे सॉकेट पर आते हैं। हम प्रत्येक तत्व में भंडारण कर रहे हैं, मूल्य नहीं, लेकिन इससे पहले की ऑफसेट। हम जैसे-जैसे आगे बढ़ते हैं, वैसे-वैसे ऑफसेट सकारात्मक होते चले जाते हैं। लेकिन ऑफसेट 8 दशमलव अंक चौड़ा हो सकता है जो 3 बाइट्स में फिट बैठता है। प्रत्येक तत्व 3 बाइट्स नहीं हो सकता है, इसलिए हमें इन्हें पैक करने की आवश्यकता है। हम प्रत्येक बाइट के शीर्ष बिट को "जारी रखें बिट" के रूप में उपयोग कर सकते हैं, यह दर्शाता है कि अगला बाइट संख्या का हिस्सा है और प्रत्येक बाइट के निचले 7 बिट्स को संयोजित करने की आवश्यकता है। डुप्लिकेट के लिए शून्य मान्य है।

जैसा कि सूची भरती है, संख्याओं को एक साथ करीब होना चाहिए, जिसका अर्थ है कि अगले मूल्य की दूरी निर्धारित करने के लिए औसतन केवल 1 बाइट का उपयोग किया जाता है। मूल्य के 7 बिट और सुविधाजनक होने पर 1 बिट ऑफ़सेट, लेकिन एक मीठा स्थान हो सकता है जिसके लिए "जारी" मान के लिए 8 बिट से कम की आवश्यकता होती है।

वैसे भी, मैंने कुछ प्रयोग किया। मैं एक यादृच्छिक संख्या जनरेटर का उपयोग करता हूं और मैं 1279000 बाइट्स में एक मिलियन सॉर्ट किए गए 8 अंकों दशमलव संख्याओं को फिट कर सकता हूं। प्रत्येक संख्या के बीच औसत स्थान लगातार 99 है ...

public class Test {
    public static void main(String[] args) throws IOException {
        // 1 million values
        int[] values = new int[1000000];

        // create random values up to 8 digits lrong
        Random random = new Random();
        for (int x=0;x<values.length;x++) {
            values[x] = random.nextInt(100000000);
        }
        Arrays.sort(values);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        int av = 0;    
        writeCompact(baos, values[0]);     // first value
        for (int x=1;x<values.length;x++) {
            int v = values[x] - values[x-1];  // difference
            av += v;
            System.out.println(values[x] + " diff " + v);
            writeCompact(baos, v);
        }

        System.out.println("Average offset " + (av/values.length));
        System.out.println("Fits in " + baos.toByteArray().length);
    }

    public static void writeCompact(OutputStream os, long value) throws IOException {
        do {
            int b = (int) value & 0x7f;
            value = (value & 0x7fffffffffffffffl) >> 7;
            os.write(value == 0 ? b : (b | 0x80));
        } while (value != 0);
    }
}

4

हम सभी संख्याएँ होने से पहले क्रमबद्ध क्रम में संख्याएँ भेजने के लिए नेटवर्किंग स्टैक के साथ खेल सकते थे। यदि आप 1M डेटा भेजते हैं, तो TCP / IP इसे 1500 बाइट पैकेट में तोड़ देगा और लक्ष्य के क्रम में स्ट्रीम कर देगा। प्रत्येक पैकेट को एक क्रम संख्या दी जाएगी।

हम इसे हाथ से कर सकते हैं। इससे पहले कि हम अपनी रैम को भरें हम क्या छांट सकते हैं और सूची को अपने लक्ष्य पर भेज सकते हैं लेकिन प्रत्येक क्रम के आसपास हमारे अनुक्रम में छेद छोड़ सकते हैं। फिर क्रम में उन छेदों का उपयोग करके संख्याओं के 2 1/2 को उसी तरह संसाधित करें।

सुदूर छोर पर नेटवर्किंग स्टैक अनुक्रम के परिणामस्वरूप परिणामी डेटा स्ट्रीम को इकट्ठा करेगा, इसे एप्लिकेशन को सौंपने से पहले।

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


4

Google का (खराब) दृष्टिकोण, HN थ्रेड से। स्टोर RLE- शैली मायने रखता है।

आपकी प्रारंभिक डेटा संरचना '99999999: 0' है (सभी शून्य, कोई संख्या नहीं देखी गई है) और फिर आपको 3,866,344 नंबर देखने की अनुमति देता है, इसलिए आपकी डेटा संरचना '3866343: 0,1: 1,96133654': 0 'हो जाती है। संख्याओं को हमेशा शून्य बिट्स की संख्या और '1' बिट्स की संख्या के बीच वैकल्पिक रूप से देखा जा सकता है ताकि आप मान सकें कि विषम संख्याएं 0 बिट्स और यहां तक ​​कि संख्या 1 बिट्स का प्रतिनिधित्व करती हैं। यह बन जाता है (3866343,1,96133654)

उनकी समस्या डुप्लिकेट को कवर करने के लिए प्रतीत नहीं होती है, लेकिन मान लें कि वे डुप्लिकेट के लिए "0: 1" का उपयोग करते हैं।

बड़ी समस्या # 1: 1M पूर्णांकों के लिए सम्मिलन में उम्र लग जाएगी

बड़ी समस्या # 2: सभी सादे डेल्टा एन्कोडिंग समाधानों की तरह, कुछ वितरणों को इस तरह से कवर नहीं किया जा सकता है। उदाहरण के लिए, 1 मीटर पूर्णांक दूरी 0:99 (उदाहरण के लिए +99 प्रत्येक)। अब एक ही सोचें, लेकिन 0:99 की सीमा में यादृच्छिक दूरी के साथ । (नोट: 99999999/1000000 = 99.99)

Google का दृष्टिकोण अयोग्य (धीमा) और गलत दोनों है। लेकिन उनके बचाव के लिए, उनकी समस्या थोड़ी अलग हो सकती थी।


3

सॉर्ट किए गए एरे को दर्शाने के लिए पहले तत्व को और आसन्न तत्वों के बीच अंतर को स्टोर कर सकते हैं। इस तरह हम 10 ^ 6 तत्वों को कूटबद्ध करने से चिंतित हैं जो कि अधिकतम 10 ^ 8 तक हो सकता है। चलो इस डी को बुलाओ । डी के तत्वों को एनकोड करने के लिए एक हफ़मैन कोड का उपयोग कर सकते हैं । हफ़मैन कोड के लिए शब्दकोश को चलते-फिरते बनाया जा सकता है और हर बार किसी नए आइटम को सॉर्ट किए गए सरणी (प्रविष्टि प्रकार) में डाला जाता है। ध्यान दें कि जब किसी नए आइटम की वजह से शब्दकोश बदलता है तो नई एन्कोडिंग से मिलान करने के लिए पूरे सरणी को अपडेट किया जाना चाहिए।

यदि हम प्रत्येक अद्वितीय तत्व की समान संख्या रखते हैं, तो D के प्रत्येक तत्व को कूटने के लिए बिट्स की औसत संख्या अधिकतम होती है। कहो तत्वों d1 , d2 , ..., dN में D प्रत्येक F बार दिखाई देते हैं । उस मामले में (सबसे खराब स्थिति में हमारे पास इनपुट अनुक्रम में 0 और 10 ^ 8 दोनों हैं)

योग (1 <= मैं <= एन ) एफdi = 10 ^ 8

कहाँ पे

योग (1 <= i <= N ) F = 10 ^ 6, या F = 10 ^ 6 / N और सामान्यीकृत आवृत्ति p = F / 10 ^ = 1 / N होगी

बिट्स की औसत संख्या -log2 (1 / P ) = log2 ( N ) होगी। इन परिस्थितियों में हमें एक ऐसा मामला खोजना चाहिए जो एन को अधिकतम करता है । यह तब होता है अगर हम के लिए लगातार नंबर हैं di 0, या से शुरू di = मैं -1, इसलिए

10 ^ 8 = राशि (1 <= i <= N ) एफdi = sum (1 <= i <= N ) (10 ^ 6 / N ) (i-1) = (10 ^ 6 / N ) N ( N -1) / 2

अर्थात

एन <= 201. और इस मामले के लिए औसत बिट्स की संख्या log2 (201) = 7.6511 है जिसका अर्थ है कि हमें क्रमबद्ध सरणी को बचाने के लिए प्रति इनपुट तत्व लगभग 1 बाइट की आवश्यकता होगी। ध्यान दें कि इसका मतलब यह नहीं है कि डी में सामान्य रूप से 201 से अधिक तत्व नहीं हो सकते हैं। यह सिर्फ यह कहता है कि यदि डी के तत्वों को समान रूप से वितरित किया जाता है, तो इसमें 201 अद्वितीय मूल्य नहीं हो सकते हैं।


1
मुझे लगता है कि आप भूल गए हैं कि संख्या डुप्लिकेट हो सकती है।
bestsss

डुप्लिकेट संख्याओं के लिए आसन्न संख्याओं के बीच का अंतर शून्य होगा। कोई समस्या पैदा नहीं करता है। हफ़मैन कोड को नॉनज़रो मान की आवश्यकता नहीं होती है।
मोहसिन नोसरतिनिया

3

मैं TCP के पुन: प्रसारण व्यवहार का फायदा उठाऊंगा।

  1. टीसीपी घटक एक बड़ी प्राप्त विंडो बनाएँ।
  2. उनके लिए एसीके भेजे बिना कुछ मात्रा में पैकेट प्राप्त करें।
    • कुछ (उपसर्ग) संकुचित डेटा संरचना बनाने वाले पास में प्रक्रिया करें
    • डुप्लीकेट ack को अंतिम पैकेट के लिए भेजें जिसकी अब कोई आवश्यकता नहीं है / पुनः प्राप्ति के समय की प्रतीक्षा करें
    • गोटो २
  3. सभी पैकेट स्वीकार किए गए

यह बाल्टी या एकाधिक पास के किसी प्रकार का लाभ मानता है।

संभवतः बैचों / बाल्टियों को छाँटकर और उन्हें मर्ज करके। -> मूलांक वृक्ष

पहले 80% को स्वीकार करने और फिर अंतिम 20% पढ़ने के लिए इस तकनीक का उपयोग करें, सत्यापित करें कि अंतिम 20% में वे संख्याएँ नहीं हैं जो सबसे कम संख्या के पहले 20% में आएँगी। फिर 20% सबसे कम नंबर भेजें, मेमोरी से हटा दें, शेष 20% नए नंबर स्वीकार करें और मर्ज करें। **


3

यहाँ इस तरह की समस्या का एक सामान्यीकृत समाधान है:

सामान्य प्रक्रिया

लिया गया दृष्टिकोण इस प्रकार है। एल्गोरिथ्म 32-बिट शब्दों के एक एकल बफर पर संचालित होता है। यह एक लूप में निम्नलिखित प्रक्रिया करता है:

  • हम पिछले पुनरावृत्ति से संकुचित डेटा से भरे एक बफर से शुरू करते हैं। बफर इस तरह दिखता है

    |compressed sorted|empty|

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

    |compressed sorted|empty|empty|

  • संख्याओं के साथ असम्पीडित अनुभाग को क्रमबद्ध भरें। बफ़र जैसा दिखता है

    |compressed sorted|empty|uncompressed unsorted|

  • इन-प्लेस सॉर्ट के साथ नए नंबर को सॉर्ट करें। बफ़र जैसा दिखता है

    |compressed sorted|empty|uncompressed sorted|

  • संपीड़ित अनुभाग में पिछले पुनरावृत्ति से किसी भी पहले से संपीड़ित डेटा को दाएं-संरेखित करें। इस बिंदु पर बफर का विभाजन किया जाता है

    |empty|compressed sorted|uncompressed sorted|

  • संपीड़ित अनुभाग पर एक स्ट्रीमिंग डीकंप्रेसन-रीकॉम्प्रेशन करें, असम्बद्ध अनुभाग में सॉर्ट किए गए डेटा में विलय। पुराने संपीड़ित अनुभाग का उपभोग किया जाता है क्योंकि नया संपीड़ित अनुभाग बढ़ता है। बफ़र जैसा दिखता है

    |compressed sorted|empty|

यह प्रक्रिया तब तक की जाती है जब तक कि सभी नंबरों को छांट न लिया जाए।

दबाव

यह एल्गोरिथ्म केवल तभी काम करता है जब नए छंटाई बफर के अंतिम संपीड़ित आकार की गणना करना संभव है इससे पहले कि वास्तव में यह जानना कि वास्तव में क्या संकुचित होगा। इसके आगे, वास्तविक समस्या को हल करने के लिए कम्प्रेशन एल्गोरिदम को पर्याप्त होना चाहिए।

प्रयुक्त दृष्टिकोण तीन चरणों का उपयोग करता है। सबसे पहले, एल्गोरिथ्म हमेशा सॉर्ट किए गए अनुक्रमों को संग्रहीत करेगा, इसलिए हम इसके बजाय लगातार प्रविष्टियों के बीच के अंतर को पूरी तरह से संग्रहीत कर सकते हैं। प्रत्येक अंतर सीमा [0, 99999999] में है।

इन मतभेदों को तब एक संयुक्त बिटस्ट्रीम के रूप में कूटबद्ध किया जाता है। इस धारा में A 1 का अर्थ है "1 को संचायक में जोड़ें, A 0 का अर्थ है" संचायक को एक प्रविष्टि के रूप में बाहर निकालें, और रीसेट करें "। इसलिए अंतर N को N 1 और एक 0 से दर्शाया जाएगा।

सभी अंतरों का योग एल्गोरिदम का समर्थन करने वाले अधिकतम मूल्य पर पहुंच जाएगा, और सभी अंतरों की गणना एल्गोरिथ्म में डाले गए मूल्यों की मात्रा तक पहुंच जाएगी। इसका अर्थ है कि हम अंत में धारा की अपेक्षा करते हैं, जिसमें अधिकतम मान 1 और गिनती 0 है। यह हमें स्ट्रीम में 0 और 1 की अनुमानित संभावना की गणना करने की अनुमति देता है। अर्थात्, 0 count/(count+maxval)की संभावना है और 1 की संभावना है maxval/(count+maxval)

इस बिटस्ट्रीम पर अंकगणित कोडिंग मॉडल को परिभाषित करने के लिए हम इन संभावनाओं का उपयोग करते हैं। यह अंकगणितीय कोड ठीक इसी मात्रा में 1 और 0 के इष्टतम स्थान को कूटबद्ध करेगा। हम किसी भी मध्यवर्ती बिटस्ट्रीम के लिए इस मॉडल द्वारा उपयोग किए गए स्थान की गणना कर सकते हैं bits = encoded * log2(1 + amount / maxval) + maxval * log2(1 + maxval / amount):। एल्गोरिथ्म के लिए कुल आवश्यक स्थान की गणना करने के लिए, encodedराशि के बराबर सेट करें ।

पुनरावृत्तियों की हास्यास्पद मात्रा की आवश्यकता नहीं करने के लिए, बफर में एक छोटा उपरि जोड़ा जा सकता है। यह सुनिश्चित करेगा कि एल्गोरिथ्म कम से कम संख्याओं की मात्रा पर काम करेगा जो इस ओवरहेड में फिट होते हैं, क्योंकि एल्गोरिथम की सबसे बड़ी समय लागत प्रत्येक चक्र में अंकगणितीय कोडिंग संपीड़न और विघटन है।

इसके आगे, कुछ ओवरहेड को बहीखाता डेटा को संग्रहीत करने और अंकगणित कोडिंग एल्गोरिथ्म के निश्चित-बिंदु सन्निकटन में थोड़ी सी अशुद्धि को संभालने के लिए आवश्यक है, लेकिन कुल मिलाकर एल्गोरिथ्म एक अतिरिक्त बफर के साथ अंतरिक्ष के 1MiB में भी फिट होने में सक्षम है जिसमें शामिल हो सकते हैं अंतरिक्ष की कुल 1043916 बाइट्स के लिए 8000 नंबर।

optimality

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


2

यदि इनपुट स्ट्रीम को कुछ बार प्राप्त किया जा सकता है तो यह बहुत आसान होगा (उस बारे में कोई जानकारी नहीं, विचार और समय-प्रदर्शन की समस्या)।

फिर, हम दशमलव मानों को गिन सकते हैं। गिने गए मूल्यों के साथ आउटपुट स्ट्रीम बनाना आसान होगा। मूल्यों की गणना करके संपीड़ित करें। यह निर्भर करता है कि इनपुट स्ट्रीम में क्या होगा।


1

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


1

छँटाई यहाँ एक माध्यमिक समस्या है। जैसा कि अन्य ने कहा, पूर्णांक को संग्रहीत करना कठिन है, और सभी इनपुट पर काम नहीं कर सकता है , क्योंकि 27 बिट प्रति संख्या आवश्यक होगी।

इस पर मेरी राय है: केवल निरंतर (सॉर्ट किए गए) पूर्णांकों के बीच के अंतरों को संग्रहीत करें, क्योंकि वे सबसे अधिक संभावना वाले छोटे होंगे। फिर एक कंप्रेशन स्कीम का उपयोग करें, जैसे कि इनपुट नंबर प्रति 2 अतिरिक्त बिट्स के साथ, यह एनकोड करने के लिए कि नंबर कितने बिट्स पर संग्रहीत है। कुछ इस तरह:

00 -> 5 bits
01 -> 11 bits
10 -> 19 bits
11 -> 27 bits

दिए गए स्मृति अवरोध के भीतर संभव इनपुट सूचियों की एक उचित संख्या को संग्रहीत करना संभव होना चाहिए। अधिकतम संख्या में इनपुट पर काम करने के लिए कंप्रेशन स्कीम को कैसे चुना जाए, इसके गणित मुझसे परे हैं।

मुझे उम्मीद है कि आप इसके आधार पर एक अच्छा पर्याप्त पूर्णांक संपीड़न योजना खोजने के लिए अपने इनपुट के डोमेन-विशिष्ट ज्ञान का दोहन ​​करने में सक्षम हो सकते हैं ।

ओह और फिर, आप उस सॉर्ट की गई सूची पर एक प्रविष्टि सॉर्ट करते हैं जैसे आप डेटा प्राप्त करते हैं।


1

अब वास्तविक समाधान का लक्ष्य रखते हुए, केवल 1 एमबी रैम के साथ 8 अंकों की सीमा में इनपुट के सभी संभावित मामलों को कवर किया गया है। नोट: कार्य प्रगति पर है, कल जारी रहेगा सॉर्ट किए गए इन्टस के डेल्टास के अंकगणित कोडिंग का उपयोग करते हुए, 1 एम सॉर्ट किए गए इनट्स के लिए सबसे खराब स्थिति प्रति एंट्री लगभग 7 बिट्स की होगी (चूंकि 99999999/1000000 99 है, और लॉग 2 (99) लगभग 7 बिट्स है)।

लेकिन आपको 7 या 8 बिट्स को प्राप्त करने के लिए 1 मी पूर्णांक की आवश्यकता है! शॉर्टर श्रृंखला में बड़ा डेल्टास होगा, इसलिए प्रति तत्व अधिक बिट्स।

मैं संभव के रूप में के रूप में कई लेने और (लगभग) में काम कर रहा हूँ। 250K के करीब ints के पहले बैच को लगभग 9 बिट्स की आवश्यकता होगी। इसलिए परिणाम लगभग 275KB होगा। शेष मुक्त मेमोरी के साथ कुछ बार दोहराएं। तब डिकम्प्रेस-मर्ज-इन-प्लेस-इन-कंप्रेस्ड विथ चंक्स। यह काफी कठिन है , लेकिन संभव है। मुझे लगता है।

मर्ज किए गए सूचियों को पूर्णांक लक्ष्य के 7 बिट के करीब और करीब मिलेगा। लेकिन मुझे नहीं पता कि यह मर्ज लूप के कितने पुनरावृत्तियों को ले जाएगा। शायद ३।

लेकिन अंकगणित कोडिंग कार्यान्वयन की असंभवता इसे असंभव बना सकती है। यदि यह समस्या बिल्कुल भी संभव है, तो यह बहुत तंग होगा।

कोई स्वयमसेवक?


अंकगणित कोडिंग व्यावहारिक है। यह नोटिस करने में मदद कर सकता है कि प्रत्येक क्रमिक डेल्टा एक नकारात्मक द्विपद वितरण से तैयार किया गया है।
भीड़

1

आपको अनुक्रम में संख्याओं के बीच अंतरों को संग्रहीत करने की आवश्यकता है, और इन अनुक्रम संख्याओं को संपीड़ित करने के लिए एन्कोडिंग का उपयोग करें। हमारे पास 2 ^ 23 बिट्स हैं। हम इसे 6 बिट विखंडू में विभाजित करेंगे, और अंतिम बिट को इंगित करते हैं कि क्या संख्या 6 बिट्स तक फैली हुई है (5 बिट प्लस फैली हुई चंक)।

इस प्रकार, 000010 1 है, और 000100 2 है। 000001100000 128 है। अब, हम 10,000,000 तक की संख्या के क्रम में अंतर का प्रतिनिधित्व करने में सबसे खराब कलाकारों पर विचार करते हैं। 2 ^ 5, 10,000,000 / 2 ^ 10 अंतर 2 ^ 10, और 10,000,000 / 2 ^ 15 अंतर 2 ^ 15 से अधिक, आदि से अधिक 10,000,000 / 2 ^ 5 अंतर हो सकते हैं।

तो, हम जोड़ते हैं कि हमारे अनुक्रम का प्रतिनिधित्व करने के लिए कितने बिट्स होंगे। हमारे पास 1,000,000 * 6 + राउंडअप (10,000,000 / 2 ^ 5) * 6 + राउंडअप (10,000,000 / 2 ^ 10) * 6 + राउंडअप (10,000,000 / 2 ^ 15) * 6 + राउंडअप (10,000,000 / 2 ^ 20) 4 = = 7,935,479।

2 ^ 24 = 8388608. 8388608> 7935479 के बाद से, हमें आसानी से पर्याप्त मेमोरी होनी चाहिए। जब हम नई संख्याएँ डालते हैं तो योग का एक और छोटा सा संग्रह करने की आवश्यकता होगी। हम फिर अनुक्रम से गुजरते हैं, और पाते हैं कि अपना नया नंबर कहां डालें, यदि आवश्यक हो तो अगले अंतर को कम करें और इसके बाद सब कुछ सही करें।


मैं विश्वास है कि अपने विश्लेषण यहाँ से पता चलता है कि इस योजना के काम नहीं करता है (और हम पांच बिट्स से एक और आकार नहीं चुन सकते हैं, भले ही)।
डैनियल वैगनर

@ डैनियल वैग्नर - आपको प्रति चंक की एक समान संख्या का उपयोग करने की आवश्यकता नहीं है, और आपको प्रति बिट के पूर्णांक संख्या का उपयोग करने की आवश्यकता नहीं है।
भीड़

यदि आप एक ठोस प्रस्ताव रखते हैं, तो मैं इसे सुनना चाहूंगा। =)
डैनियल वैगनर

@ क्राउडिंग गणित को करें कि अंकगणितीय कोडिंग में कितनी जगह होगी। थोड़ा रोओ। फिर और कठिन सोचो।
डेनियल वैगनर

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

1

यदि हमें उन संख्याओं के बारे में कुछ नहीं पता है, तो हम निम्नलिखित बाधाओं से सीमित हैं:

  • इससे पहले कि हम उन्हें सॉर्ट कर सकें, हमें सभी नंबरों को लोड करना होगा,
  • संख्याओं का सेट संपीड़ित नहीं है।

यदि ये धारणाएं चलती हैं, तो अपना काम पूरा करने का कोई तरीका नहीं है, क्योंकि आपको कम से कम 26,575,425 बिट्स स्टोरेज (3,321,929 बाइट्स) की आवश्यकता होगी।

आप हमें अपने डेटा के बारे में क्या बता सकते हैं?


1
आप उन्हें पढ़ते हैं और जाते ही उन्हें छांटते हैं। सैद्धांतिक रूप से इसमें lg2 (100999999! / (99999999! * 1000000!)) बिट्स की आवश्यकता होती है, जो 100M प्रतिष्ठित बॉक्स में 1M अप्रभेद्य वस्तुओं को स्टोर कर सके, जो 1MB के 96.4% तक काम करता है।
नोवाडेनिज़ेन

1

चाल एल्गोरिदम राज्य का प्रतिनिधित्व करने के लिए है, जो एक पूर्णांक बहु-सेट है, "वृद्धि काउंटर" = "+" और "आउटपुट काउंटर" = "!" पात्र। उदाहरण के लिए, सेट {0,3,3,4} को "! +++ !! +!" के रूप में दर्शाया जाएगा, इसके बाद किसी भी संख्या में "+" अक्षर। मल्टी-सेट को संशोधित करने के लिए आप पात्रों को स्ट्रीम करते हैं, केवल एक स्थिर राशि को एक बार में विघटित करते हैं, और उन्हें संपीड़ित रूप में वापस स्ट्रीम करने से पहले परिवर्तन की जगह बनाते हैं।

विवरण

हम जानते हैं कि अंतिम सेट में ठीक 10 ^ 6 नंबर हैं, इसलिए अधिकतम 10 ^ 6 "हैं!" पात्र। हम यह भी जानते हैं कि हमारी सीमा का आकार 10 ^ 8 है, जिसका अर्थ है कि अधिकतम 10 ^ 8 "+" वर्ण हैं। तरीकों की संख्या हम 10 ^ 6 "की व्यवस्था कर सकते हैं!" 10 ^ 8 "+" के बीच है (10^8 + 10^6) choose 10^6, और इसलिए कुछ विशेष व्यवस्था निर्दिष्ट करने में ~ 0.965 MiB लगता है डेटा का ` । यह एक चुस्त दुरुस्त होगा।

हम अपने कोटे को पार किए बिना प्रत्येक वर्ण को स्वतंत्र मान सकते हैं। वहाँ "से 100 गुना अधिक" + "वर्ण हैं!" वर्ण, जो 100 को सरल करता है: 1 प्रत्येक वर्ण का एक "" + होने के कारण यदि हम भूल जाते हैं कि वे निर्भर हैं। 100 के औसत: 101 ~ चरित्र के 0.08 बिट्स से मेल खाती है , ~ 0.965 MiB के लगभग समान कुल के लिए (निर्भरता की अनदेखी इस मामले में केवल ~ 12 बिट्स की लागत है !)।

ज्ञात पूर्व संभावना के साथ स्वतंत्र पात्रों को संग्रहीत करने की सबसे सरल तकनीक हफ़मैन कोडिंग है । ध्यान दें कि हमें एक अव्यवस्थित रूप से बड़े पेड़ की आवश्यकता है (10 वर्णों के ब्लॉक के लिए एक हफ़मैन पेड़ में प्रति 2.4 बिट के लिए औसतन प्रति ब्लॉक औसत कीमत है। 2.9 मिब के लिए। 20 वर्णों के ब्लॉक के लिए एक हफ़मैन पेड़ की औसत लागत प्रति ब्लॉक है। के बारे में 3 बिट्स, जो कुल ~ 1.8 MiB है। हमें शायद सौ के क्रम पर आकार की एक ब्लॉक की आवश्यकता है, जो हमारे पेड़ में उन सभी कंप्यूटर उपकरणों की तुलना में अधिक नोड्स लगाते हैं जो कभी अस्तित्व में हैं, स्टोर कर सकते हैं। )। हालाँकि, समस्या के अनुसार ROM तकनीकी रूप से "मुक्त" है और पेड़ में नियमितता का लाभ उठाने वाले व्यावहारिक समाधान अनिवार्य रूप से समान दिखेंगे।

छद्म कोड

  • रोम में संग्रहीत पर्याप्त रूप से बड़े हफ़मैन वृक्ष (या समान ब्लॉक-बाय-ब्लॉक संपीड़न डेटा) रखें
  • 10 ^ 8 "+" वर्णों के संकुचित स्ट्रिंग के साथ शुरू करें।
  • संख्या N को सम्मिलित करने के लिए, संपीड़ित स्ट्रिंग को तब तक प्रवाहित करें जब तक N "+" वर्ण अतीत में नहीं चले जाते हैं तब "" डालें। पिछले एक से अधिक बार वापस जाने पर स्ट्रिंग को स्ट्रीम करें, बफ़र किए गए ब्लॉकों की निरंतर मात्रा को ओवर / अंडर-रनों से बचने के लिए रखें।
  • एक मिलियन बार दोहराएं: [इनपुट, स्ट्रीम डीकंप्रेस> इंसर्ट> कंप्रेस], फिर डीकंप्रेस टू आउटपुट

1
अब तक, यह एकमात्र उत्तर है जो मैं देखता हूं कि वास्तव में समस्या का जवाब है! मुझे लगता है कि अंकगणित कोडिंग, हफमैन कोडिंग की तुलना में अधिक सरल है, हालांकि यह एक कोडबुक को संग्रहीत करने और प्रतीक सीमाओं के बारे में चिंता करने की आवश्यकता को कम करता है। आप निर्भरता के लिए भी जिम्मेदार हो सकते हैं।
भीड़

इनपुट पूर्णांक सॉर्ट नहीं किए गए हैं। आपको पहले छांटने की जरूरत है।
alecco

1
@alecco एल्गोरिथ्म उन्हें क्रमबद्ध करते हुए आगे बढ़ता है। वे कभी भी भंडारित नहीं होते।
क्रेग गिदनी

1

हमारे पास 1 एमबी - 3 केबी रैम = 2 ^ 23 - 3 * 2 ^ 13 बिट्स = 8388608 - 24576 = 8364032 बिट्स उपलब्ध हैं।

हमें 10 ^ 8 रेंज में 10 ^ 6 नंबर दिए गए हैं। यह ~ 100 <2 ^ 7 = 128 का औसत अंतर देता है

आइए सबसे पहले सभी समान अंतरालों के लिए समान रूप से समान संख्याओं की सरल समस्या पर विचार करें। यह आसान है। बस पहला नंबर और 7-बिट अंतराल स्टोर करें:

(27 बिट्स) + 10 ^ 6 7-बिट गैप संख्या = 7000027 बिट्स की आवश्यकता

नोट दोहराया संख्या में 0 के अंतराल हैं।

लेकिन क्या होगा अगर हमारे पास 127 से बड़ा अंतराल है?

ठीक है, मान लें कि अंतराल का आकार <127 सीधे दर्शाया गया है, लेकिन 127 के अंतराल के आकार के बाद वास्तविक अंतराल लंबाई के लिए निरंतर 8-बिट एन्कोडिंग है:

 10xxxxxx xxxxxxxx                       = 127 .. 16,383
 110xxxxx xxxxxxxx xxxxxxxx              = 16384 .. 2,097,151

आदि।

ध्यान दें कि यह संख्या निरूपण इसकी अपनी लंबाई का वर्णन करता है इसलिए हमें पता है कि अगला अंतराल संख्या कब शुरू होती है।

केवल छोटे अंतराल <127 के साथ, इसके लिए अभी भी 7000027 बिट्स की आवश्यकता है।

इसमें (10 ^ 8) / (2 ^ 7) = 781250 23-बिट गैप संख्या हो सकती है, इसके लिए 16 * 781,250 = 12,500,000 बिट्स की आवश्यकता होती है जो बहुत अधिक है। हमें अंतराल के लिए एक अधिक कॉम्पैक्ट और धीरे-धीरे बढ़ते प्रतिनिधित्व की आवश्यकता है।

औसत अंतर का आकार 100 है इसलिए यदि हम उन्हें [100, 99, 101, 98, 102, ..., 2, 198, 1, 199, 0, 200, 201, 202, ...] के रूप में पुन: व्यवस्थित करते हैं और इसे अनुक्रमित करते हैं। शून्य के बिना जोड़े के साथ घने बाइनरी फाइबोनैचि बेस एन्कोडिंग के साथ (उदाहरण के लिए, 11011 = 8 + 5 + 2 + 1 = 16) संख्याओं के साथ '00' द्वारा सीमांकित किया गया, तो मुझे लगता है कि हम अंतर को बहुत कम रख सकते हैं, लेकिन इसकी आवश्यकता है अधिक विश्लेषण।


0

स्ट्रीम प्राप्त करते समय इन चरणों को करें।

1 ने कुछ उचित चंक आकार निर्धारित किए

छद्म कोड विचार:

  1. पहला कदम यह होगा कि सभी डुप्लिकेट ढूंढे जाएं और उन्हें इसकी गिनती के साथ एक शब्दकोश में चिपका दें और उन्हें हटा दें।
  2. तीसरा कदम संख्या को उनके एल्गोरिदमिक चरणों के क्रम में मौजूद स्थान पर रखना होगा और उन्हें पहले नंबर और उनके चरण जैसे n, n + 1 ..., n + 2, 2n, 2n + 1 के साथ काउंटरों विशेष शब्दकोशों में रखना होगा। 2n + 2 ...
  3. प्रत्येक 1000 या कभी 10000 शेष संख्याओं की कुछ उचित श्रेणियों को विखंडन में संपीड़ित करना शुरू करें जो कि दोहराने के लिए कम बार दिखाई देते हैं।
  4. यदि कोई संख्या पाई जाती है तो उस श्रेणी को अनकम्प्रेस करें और इसे सीमा में जोड़ें और इसे थोड़ी देर के लिए असम्पीडित छोड़ दें।
  5. अन्यथा बस उस नंबर को बाइट में जोड़ें [chunkSize]

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

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