नेस्टेड छोरों के लिए तेज़ विकल्प?


85

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

var data = new List<byte[]>();
for (byte a = 0; a < 2; a++)
for (byte b = 0; b < 3; b++)
for (byte c = 0; c < 4; c++)
for (byte d = 0; d < 3; d++)
for (byte e = 0; e < 4; e++)
for (byte f = 0; f < 3; f++)
for (byte g = 0; g < 3; g++)
for (byte h = 0; h < 4; h++)
for (byte i = 0; i < 2; i++)
for (byte j = 0; j < 4; j++)
for (byte k = 0; k < 4; k++)
for (byte l = 0; l < 3; l++)
for (byte m = 0; m < 4; m++)
{
    data.Add(new [] {a, b, c, d, e, f, g, h, i, j, k, l, m});
}

मैं कुछ का उपयोग करने पर विचार कर रहा था, BitArrayलेकिन मुझे यकीन नहीं है कि मैं इसे कैसे शामिल कर सकता हूं।

प्रत्येक अनुशंसा के लिए हम आपके अत्यंत आभारी होंगे। वैकल्पिक रूप से, शायद यह सबसे तेज़ तरीका है जो मुझे चाहिए?

त्वरित बिंदुओं की EDIT जोड़ी (और माफी जो मैंने उन्हें मूल पोस्ट में नहीं डाली थी):

  • संख्या और उनके क्रम (2, 3, 4, 3, 4, 3, 3 आदि) बहुत महत्वपूर्ण हैं, इसलिए एक समाधान का उपयोग करना जैसे कि LINQ का उपयोग करके जनरेटिंग क्रमांकन में मदद नहीं मिलेगी क्योंकि प्रत्येक 'कॉलम' में मैक्सिमम हैं विभिन्न
  • मैं गणितज्ञ नहीं हूं, इसलिए मैं माफी मांगता हूं अगर मैं 'क्रमपरिवर्तन' और 'संयोजन' जैसे तकनीकी शब्दों का उपयोग नहीं कर रहा हूं
  • मैं है मैं सिर्फ एक आकर्षित नहीं कर सकते हैं या किसी अन्य एक सूचकांक के आधार पर - एक बार में इन संयोजनों के सभी से पॉप्युलेट करने
  • उपयोग करने byteकी तुलना में उपयोग करना तेज है int, मैं इसकी गारंटी देता हूं । यह भी स्मृति उपयोग पर एक बहुत बेहतर है बट्स के बजाय बाइट्स के 67m + सरणियों है
  • मेरा अंतिम लक्ष्य नेस्टेड छोरों के लिए एक तेज विकल्प की तलाश करना है।
  • मैंने समानांतर प्रोग्रामिंग का उपयोग करने पर विचार किया, लेकिन जो मैं हासिल करने की कोशिश कर रहा हूं, उसके पुनरावृत्ति स्वभाव के कारण, मैं इसे सफलतापूर्वक (यहां तक ​​कि ConcurrentBag) करने का एक तरीका नहीं खोज सका - हालांकि मैं गलत साबित होने के लिए खुश हूं :)

निष्कर्ष

Caramiriel ने एक अच्छा माइक्रो-ऑप्टिमाइज़ेशन प्रदान किया है जो लूप्स से कुछ समय दूर होता है, इसलिए मैंने उस उत्तर को सही के रूप में चिह्नित किया है। एरिक ने यह भी उल्लेख किया कि सूची को पूर्व-आवंटित करना तेज है। लेकिन, इस स्तर पर ऐसा लगता है कि नेस्टेड लूप वास्तव में ऐसा करने का सबसे तेज़ संभव तरीका है (निराशाजनक, मुझे पता है!)।

यदि आप वास्तव में कोशिश करना चाहते हैं कि मैं किसके साथ बेंचमार्क करने की कोशिश कर रहा था StopWatch, तो प्रत्येक लूप में 4 तक गिनती के साथ 13 छोरों पर जाएं - जो सूची में लगभग 67m + लाइनें बनाता है। मेरी मशीन (i5-3320M 2.6GHz) पर अनुकूलित संस्करण को करने में लगभग 2.2s का समय लगता है।


1
Linq का उपयोग करने का प्रयास करें और यदि आप मल्टीकोर प्रोसेसर का उपयोग कर रहे हैं तो Parrallel.for
जलपेश वडगामा

1
जो मैं देख रहा हूं उसके आधार पर ये क्रमपरिवर्तन नहीं हैं, लेकिन बहुत छोटे (2-4 तत्वों) सेटों के एक जोड़े का संयोजन सही है या क्या आप वास्तव में एक सेट के सभी / कुछ क्रमपरिवर्तन चाहते हैं ?
कार्स्टन

मुझे लगता है कि आपने पहले ही खोज कर ली थी और bing.com/search?q=c%23+permutation+enumerable पहले से ही और किसी कारण से (पोस्ट में उल्लेख नहीं किया गया है) stackoverflow.com/questions/4319049// जैसे मौजूदा उत्तरों के खिलाफ निर्णय लिया है ... सूची पर विचार करें इस प्रश्न को बेहतर बनाने के लिए आपने जिन विकल्पों को देखा और उनके विरुद्ध निर्णय लिया।
अलेक्सई लेवेनकोव

3
यदि यह प्रदर्शन के बारे में है: तो आप सूची का निर्माण (निर्माता) कर सकते हैं और कुछ लूपों को अनियंत्रित कर सकते हैं, लेकिन मुझे लगता है कि इसके बारे में ... इसके अलावा इन नंबरों को पूर्वगामी और संग्रहीत करने से। लूप (ओवरहेड) संभवतः उन सभी की सबसे महंगी हैं, क्योंकि शरीर के अंदर कम मात्रा में ऑपरेशन होते हैं।
कैरामिरियल

5
@ वेबपेज: आपको सभी संयोजनों को सामने लाने की आवश्यकता क्यों है? जब आपको इसकी आवश्यकता होती है तो इसके सूचकांक से संयोजन क्यों नहीं बनाया जाता है?
पीटर विटोवेट

जवाबों:


61

आप एक संरचना के गुणों का उपयोग कर सकते हैं और अग्रिम में संरचना आवंटित कर सकते हैं। मैंने नीचे के नमूने में कुछ स्तरों को काट दिया है, लेकिन मुझे यकीन है कि आप बारीकियों का पता लगाने में सक्षम होंगे। मूल (रिलीज़ मोड) की तुलना में लगभग 5-6 गुना तेज दौड़ता है।

खंड:

struct ByteBlock
{
    public byte A;
    public byte B;
    public byte C;
    public byte D;
    public byte E;
}

सूचित करते रहना:

var data = new ByteBlock[2*3*4*3*4];
var counter = 0;

var bytes = new ByteBlock();

for (byte a = 0; a < 2; a++)
{
    bytes.A = a;
    for (byte b = 0; b < 3; b++)
    {
        bytes.B = b;
        for (byte c = 0; c < 4; c++)
        {
            bytes.C = c;
            for (byte d = 0; d < 3; d++)
            {
                bytes.D = d;
                for (byte e = 0; e < 4; e++)
                {
                    bytes.E = e;
                    data[counter++] = bytes;
                }
            }
        }
    }
}

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

साइड-इफेक्ट्स के लिए टिप्पणियाँ भी पढ़ें।

के T[]बजाय उपयोग करने के लिए उत्तर का संपादन किया List<T>


1
इसकी एक संरचना है, इसलिए आपको ठीक होना चाहिए =) वे सभी अद्वितीय हैं। List<T>.Addविधि को कॉल करने पर इसे कॉपी किया जाता है।
कैरामिरियल

4
यह और भी तेजी से अगर आपने सूची ()
एरिक

5
से सावधान रहें stackoverflow अपवाद स्टैक पर भी कई वस्तुओं का आवंटन।
आंद्रेई टाल्टर

7
@ और मैंने आपकी बात नहीं मानी। यह कोड पुनरावर्ती नहीं है और इसमें न्यूनतम स्टैक का उपयोग होता है।
कोडइन्कॉचो

3
@ भारत: स्मृति से बाहर, नहीं stackoverflow। ऐसा इसलिए है क्योंकि यह List<T>.Add()विधि इस बात से परे है कि यह क्या स्टोर कर सकता है। यह इसका आकार बदल देगा (आकार में दोगुना), जो 2GB से अधिक मेमोरी में चला जाता है। नई सूची <ByteBlock> (maxPerLevel.Aggregate (1, y) => x * y)) का उपयोग करके प्रचार करने का प्रयास करें, हालांकि इसके पहले से ही 'यादृच्छिक' है कि आपको इस डेटा की पूर्ण 2GB ब्लॉक की आवश्यकता है स्मृति में। यह भी ध्यान रखें कि डेटा। महंगा है क्योंकि यह उस समय दो बार मेमोरी में आइटम रखता है। [
पुनःप्रकाशित

33

आप जो कर रहे हैं वह गिनती है (चर मूलांक के साथ, लेकिन फिर भी गिनती)।

जब से आप C # का उपयोग कर रहे हैं, मुझे लगता है कि आप उपयोगी मेमोरी लेआउट और डेटा संरचनाओं के साथ नहीं खेलना चाहते हैं जो आपको वास्तव में अपने कोड को अनुकूलित करने की अनुमति देते हैं ।

इसलिए यहां मैं कुछ अलग पोस्ट कर रहा हूं, जो आपके मामले के अनुरूप नहीं हो सकता है, लेकिन यह ध्यान देने योग्य है: यदि आप वास्तव में एक विरल फैशन में सूची तक पहुंचते हैं, तो यहां एक वर्ग है जो आपको रैखिक समय में आई-वें तत्व की गणना करने देता है (बल्कि घातांक की तुलना में अन्य उत्तर)

class Counter
{
    public int[] Radices;

    public int[] this[int n]
    {
        get 
        { 
            int[] v = new int[Radices.Length];
            int i = Radices.Length - 1;

            while (n != 0 && i >= 0)
            {
                //Hope C# has an IL-opcode for div-and-reminder like x86 do
                v[i] = n % Radices[i];
                n /= Radices[i--];
            }
            return v;
        }
    }
}

आप इस वर्ग का उपयोग इस तरह से कर सकते हैं

Counter c = new Counter();
c.Radices = new int[] { 2,3,4,3,4,3,3,4,2,4,4,3,4};

अब c[i]अपनी सूची के रूप में एक ही है, इसे नाम l, l[i]

जैसा कि आप देख सकते हैं, आप आसानी से उन सभी छोरों से बच सकते हैं :) यहां तक ​​कि जब आप पूरी सूची को पूरी तरह से पूर्व कर देते हैं तब से आप केवल कैरी-रिपल काउंटर को लागू कर सकते हैं।

काउंटर्स एक बहुत ही अध्ययन का विषय है, अगर आपको लगता है कि मैं कुछ साहित्य की खोज करने की दृढ़ता से सलाह देता हूं।


4
मुझे आपका उत्तर पसंद है, लेकिन यह कहना कि अन्य सभी उत्तर घातीय हैं, असत्य है।
बिस्कुट

1
Caramiriel के उत्तर की तुलना में इस पर गति क्या है?
जॉन ओडोम

17
"सी-किडनी- #", सच में? यह पूरी तरह से अनकम्फर्टेबल लगता है।
KChaloux

2
और यह करता है: Math.DivRem
Caramiriel

1
मुझे लगता है कि कुछ स्तर पर काम करना, ऑप्टिमाइज़ेशन उपयोग का विषय है। उदाहरण के लिए, यदि प्रत्येक सरणी को केवल एक बार उपयोग किया जाना है, तो आप गहन मेमोरी आवंटन से बच सकते हैं, जो कि मेरी राय में महत्वपूर्ण अड़चन है। इसके अलावा, यदि आप सभी मानों की गणना करना चाहते हैं, तो आपको इस तथ्य का फायदा उठाना चाहिए कि आप एक विभाजन से बचने के लिए एकल वेतन वृद्धि (यानी +1 वेतन वृद्धि) करते हैं। यह "आउट ऑफ द बॉक्स" आंसर या एक प्रोटोटाइप के रूप में अधिक इरादा है, मैंने वास्तव में इसे गति देने की कोशिश नहीं की, मुझे यह इस तरह से पसंद है :)

14

विधि 1

इसे तेज़ करने का एक तरीका यह है कि यदि आप List<byte[]>इस तरह का उपयोग करने की योजना बनाते हैं, तो क्षमता को निर्दिष्ट करना है ।

var data = new List<byte[]>(2 * 3 * 4 * 3 * 4 * 3 * 3 * 4 * 2 * 4 * 4 * 3 * 4);

विधि 2

इसके अलावा, आप System.Arrayतेजी से पहुंच प्राप्त करने के लिए सीधे उपयोग कर सकते हैं । मैं इस दृष्टिकोण की सिफारिश करता हूं यदि आपका प्रश्न जोर देकर कहता है कि हर तत्व को भौतिक रूप से मेमोरी, अपफ्रंट में आबादी है।

var data = new byte[2 * 3 * 4 * 3 * 4 * 3 * 3 * 4 * 2 * 4 * 4 * 3 * 4][];
int counter = 0;

for (byte a = 0; a < 2; a++)
    for (byte b = 0; b < 3; b++)
        for (byte c = 0; c < 4; c++)
            for (byte d = 0; d < 3; d++)
                for (byte e = 0; e < 4; e++)
                    for (byte f = 0; f < 3; f++)
                        for (byte g = 0; g < 3; g++)
                            for (byte h = 0; h < 4; h++)
                                for (byte i = 0; i < 2; i++)
                                    for (byte j = 0; j < 4; j++)
                                        for (byte k = 0; k < 4; k++)
                                            for (byte l = 0; l < 3; l++)
                                                for (byte m = 0; m < 4; m++)
                                                    data[counter++] = new[] { a, b, c, d, e, f, g, h, i, j, k, l, m };

यह मेरे कंप्यूटर पर पूरा करने के लिए 596 एमएस लेता है , जो प्रश्न में कोड की तुलना में लगभग 10.4% तेज है (जो 658ms लेता है)।

विधि 3

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

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

class HypotheticalBytes
{
    private readonly int _c1, _c2, _c3, _c4, _c5, _c6, _c7, _c8, _c9, _c10, _c11, _c12;
    private readonly int _t0, _t1, _t2, _t3, _t4, _t5, _t6, _t7, _t8, _t9, _t10, _t11;

    public int Count
    {
        get { return _t0; }
    }

    public HypotheticalBytes(
        int c0, int c1, int c2, int c3, int c4, int c5, int c6, int c7, int c8, int c9, int c10, int c11, int c12)
    {
        _c1 = c1;
        _c2 = c2;
        _c3 = c3;
        _c4 = c4;
        _c5 = c5;
        _c6 = c6;
        _c7 = c7;
        _c8 = c8;
        _c9 = c9;
        _c10 = c10;
        _c11 = c11;
        _c12 = c12;
        _t11 = _c12 * c11;
        _t10 = _t11 * c10;
        _t9 = _t10 * c9;
        _t8 = _t9 * c8;
        _t7 = _t8 * c7;
        _t6 = _t7 * c6;
        _t5 = _t6 * c5;
        _t4 = _t5 * c4;
        _t3 = _t4 * c3;
        _t2 = _t3 * c2;
        _t1 = _t2 * c1;
        _t0 = _t1 * c0;
    }

    public byte[] this[int index]
    {
        get
        {
            return new[]
            {
                (byte)(index / _t1),
                (byte)((index / _t2) % _c1),
                (byte)((index / _t3) % _c2),
                (byte)((index / _t4) % _c3),
                (byte)((index / _t5) % _c4),
                (byte)((index / _t6) % _c5),
                (byte)((index / _t7) % _c6),
                (byte)((index / _t8) % _c7),
                (byte)((index / _t9) % _c8),
                (byte)((index / _t10) % _c9),
                (byte)((index / _t11) % _c10),
                (byte)((index / _c12) % _c11),
                (byte)(index % _c12)
            };
        }
    }
}

यह मेरे कंप्यूटर पर ( विधि 2 में एक के रूप में भी बनाने और जोड़ने ) को पूरा करने के लिए 897 एमएस लेता है , जो कि प्रश्न में कोड की तुलना में 36.3% धीमा है (जो 658ms लेता है)।Array


1
स्मृति खपत के मामले में आपका दूसरा सुझाव भी एक महत्वपूर्ण बचत है। (लेकिन मैं ध्यान
दूंगा

मुझे एक बार में पूरी सूची बनाने की आवश्यकता है - मैं सूची के भीतर एक सूचकांक का उल्लेख नहीं कर सकता।
बेन्पेज

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

3
@benpage आपको आबादी की सूची की आवश्यकता क्यों है?
तैमूर

14

मेरी मशीन पर, यह 222 एमएस बनाम 760 एमएस (लूप के लिए 13) में संयोजन उत्पन्न करता है:

private static byte[,] GenerateCombinations(byte[] maxNumberPerLevel)
{
    var levels = maxNumberPerLevel.Length;

    var periodsPerLevel = new int[levels];
    var totalItems = 1;
    for (var i = 0; i < levels; i++)
    {
        periodsPerLevel[i] = totalItems;
        totalItems *= maxNumberPerLevel[i];
    }

    var results = new byte[totalItems, levels];

    Parallel.For(0, levels, level =>
    {
        var periodPerLevel = periodsPerLevel[level];
        var maxPerLevel = maxNumberPerLevel[level];
        for (var i = 0; i < totalItems; i++)
            results[i, level] = (byte)(i / periodPerLevel % maxPerLevel);
    });

    return results;
}

यह एक महान जवाब है! दुर्भाग्य से यह नेस्टेड छोरों की तुलना में धीमी गति से चलता है। कोई भी मौका जिसे आप TPL का उपयोग करके संपादित कर सकते हैं?
बेन्पेज

अभी भी थोड़ा धीमा, दुर्भाग्य से।
बेन्पेज

1
@benpage इसे कम से कम 2 गुना तेज बनाने का एक आसान तरीका है। आपको केवल परिणाम प्रकार को int [,] में बदलना होगा। यह एक कॉल में संपूर्ण सरणी मेमोरी आवंटित करेगा। मुझे यकीन नहीं है कि यह आपकी आवश्यकताओं को कैसे फिट करता है (रिटर्न प्रकार को बदलना)।
आंद्रेई टाल्टर

8
var numbers = new[] { 2, 3, 4, 3, 4, 3, 3, 4, 2, 4, 4, 3, 4 };
var result = (numbers.Select(i => Enumerable.Range(0, i))).CartesianProduct();

Http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/ पर विस्तार विधि का उपयोग करना

public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
    // base case: 
    IEnumerable<IEnumerable<T>> result =
        new[] { Enumerable.Empty<T>() };
    foreach (var sequence in sequences)
    {
        // don't close over the loop variable (fixed in C# 5 BTW)
        var s = sequence;
        // recursive case: use SelectMany to build 
        // the new product out of the old one 
        result =
            from seq in result
            from item in s
            select seq.Concat(new[] { item });
    }
    return result;
}

1
यह बहुत धीमी गति से चलता है :(
बेंज पेज

8

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

चूंकि आप पहले से ही तत्वों की संख्या जानते हैं, इसलिए आप सही आकार की सूची बना सकते हैं, जो पहले से ही बहुत तेज होना चाहिए।

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


मैंने वास्तव में एक नियमित सरणी को पूर्व-आवंटित करने की कोशिश की है और यह विश्वास है कि यह धीमा है या नहीं। जैसा कि मैंने ऊपर कहा था, इसे मक्खी पर बनाने की जरूरत है, मैं इसे एक बार की गणना नहीं कर सकता और इसे छोड़ सकता हूं।
बेनोपेज

वास्तव में? वाह - क्या आप अनुकूलन के साथ चल रहे हैं? (बस पूछते हुए)
कार्स्टन

आह यह एक और मुद्दा है, नियमित सरणियाँ [x, y] का उपयोग करना अच्छा है, लेकिन सरणियों की एक सरणी तेजी से होगी। stackoverflow.com/questions/597720/… क्योंकि वे कैसे आईएल में हुड के नीचे लागू कर रहे हैं
gjvdkamp

5

यहां एक अलग तरीका है जिसे केवल 2 लूप की आवश्यकता है। विचार पहले तत्व को बढ़ाने के लिए है और यदि वह संख्या अगले एक को बढ़ाने से अधिक है।

डेटा प्रदर्शित करने के बजाय, आप currentValues.Clone का उपयोग कर सकते हैं और उस क्लोन संस्करण को अपनी सूची में जोड़ सकते हैं। मेरे लिए यह आपके संस्करण से अधिक तेज़ी से चला।

byte[] maxValues = {2, 3, 4};
byte[] currentValues = {0, 0, 0};

do {
    Console.WriteLine("{0}, {1}, {2}", currentValues[0], currentValues[1], currentValues[2]);

    currentValues[0] += 1;

    for (int i = 0; i <= maxValues.Count - 2; i++) {
        if (currentValues[i] < maxValues[i]) {
            break;
        }

        currentValues[i] = 0;
        currentValues[i + 1] += 1;
    }

// Stop the whole thing if the last number is over
// } while (currentValues[currentValues.Length-1] < maxValues[maxValues.Length-1]);
} while (currentValues.Last() < maxValues.Last());
  • आशा है कि यह कोड काम करता है, मैंने इसे vb से बदल दिया है

3

आपके सभी नंबर लगातार समय संकलित हैं।

एक सूची में सभी छोरों को अनियंत्रित करने के बारे में क्या है (कोड लिखने के लिए अपने कार्यक्रम का उपयोग करके):

data.Add(new [] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
data.Add(new [] {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
etc.

कम से कम छोरों के ओवरहेड को दूर करना चाहिए (यदि कोई है)।

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


बॉक्स एप्रोच के बाहर सीरियललाइज़ेशन वास्तव में बहुत अच्छी सोच है!
जोएल बी

दुर्भाग्य से सूची में अधिकतम डायनामिक है, मैं इसे सांख्यिकीय रूप से टाइप नहीं कर सकता। हालांकि अच्छा विचार है!
बेन्पेज पेज

2

क्या आपको सरणियों की एक सरणी होने के लिए परिणाम की आवश्यकता है? वर्तमान सेटअप के साथ आंतरिक सरणियों की लंबाई तय हो गई है और इसे संरचना के साथ बदल दिया जा सकता है। यह पूरी चीज़ को मेमोरी के एक निरंतर ब्लॉक के रूप में आरक्षित करने की अनुमति देता है और तत्वों तक आसान पहुँच प्रदान करता है (यह सुनिश्चित नहीं है कि आप बाद में इस चीज़ का उपयोग कैसे करते हैं)।

नीचे का दृष्टिकोण बहुत तेज़ है (मेरे बॉक्स पर मूल के लिए 41ms बनाम 1071ms):

struct element {
    public byte a;
    public byte b;
    public byte c;
    public byte d;
    public byte e;
    public byte f;
    public byte g;
    public byte h;
    public byte i;
    public byte j;
    public byte k;
    public byte l;
    public byte m;
}

element[] WithStruct() {
    var t = new element[3981312];
    int z = 0;
    for (byte a = 0; a < 2; a++)
    for (byte b = 0; b < 3; b++)
    for (byte c = 0; c < 4; c++)
    for (byte d = 0; d < 3; d++)
    for (byte e = 0; e < 4; e++)
    for (byte f = 0; f < 3; f++)
    for (byte g = 0; g < 3; g++)
    for (byte h = 0; h < 4; h++)
    for (byte i = 0; i < 2; i++)
    for (byte j = 0; j < 4; j++)
    for (byte k = 0; k < 4; k++)
    for (byte l = 0; l < 3; l++)
    for (byte m = 0; m < 4; m++)
    {
        t[z].a = a;
        t[z].b = b;
        t[z].c = c;
        t[z].d = d;
        t[z].e = e;
        t[z].f = f;
        t[z].g = g;
        t[z].h = h;
        t[z].i = i;
        t[z].j = j;
        t[z].k = k;
        t[z].l = l;
        t[z].m = m;
        z++;
    }
    return t;
}

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

1

Parallel.For()इसे चलाने के लिए उपयोग करने के बारे में क्या ? ( संरचना अनुकूलन यश @Caramiriel के लिए )। मैंने मूल्यों को थोड़ा संशोधित किया है (2 के बजाय ए 5 है) इसलिए मैं परिणामों में अधिक आश्वस्त हूं।

    var data = new ConcurrentStack<List<Bytes>>();
    var sw = new Stopwatch();

    sw.Start();

    Parallel.For(0, 5, () => new List<Bytes>(3*4*3*4*3*3*4*2*4*4*3*4),
      (a, loop, localList) => {
        var bytes = new Bytes();
        bytes.A = (byte) a;
        for (byte b = 0; b < 3; b++) {
          bytes.B = b;
          for (byte c = 0; c < 4; c++) {
            bytes.C = c; 
            for (byte d = 0; d < 3; d++) {
              bytes.D = d; 
              for (byte e = 0; e < 4; e++) {
                bytes.E = e; 
                for (byte f = 0; f < 3; f++) {
                  bytes.F = f; 
                  for (byte g = 0; g < 3; g++) {
                    bytes.G = g; 
                    for (byte h = 0; h < 4; h++) {
                      bytes.H = h; 
                      for (byte i = 0; i < 2; i++) {
                        bytes.I = i; 
                        for (byte j = 0; j < 4; j++) {
                          bytes.J = j; 
                          for (byte k = 0; k < 4; k++) {
                            bytes.K = k; 
                            for (byte l = 0; l < 3; l++) {
                              bytes.L = l;
                              for (byte m = 0; m < 4; m++) {
                                bytes.M = m;
                                localList.Add(bytes);
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }


        return localList;
      }, x => {
        data.Push(x);
    });

    var joinedData = _join(data);

_join() एक निजी विधि है, जिसे इस प्रकार परिभाषित किया गया है:

private static IList<Bytes> _join(IEnumerable<IList<Bytes>> data) {
  var value = new List<Bytes>();
  foreach (var d in data) {
    value.AddRange(d);
  }
  return value;
}

मेरे सिस्टम पर, यह संस्करण लगभग 6 गुना तेज (1.718 सेकंड बनाम 0.266 सेकंड) चलाता है।


1
यह आपको झूठी साझेदारी देने के लिए बहुत अधिक गारंटी देता है और शायद कई बार धीमा हो जाएगा।
gjvdkamp

बुरा नहीं है - दुर्भाग्य से यह छोरों की तुलना में धीमा चलता है। FWIW मैं यह सब के साथ की कोशिश की Parallel.Forऔर वी.एस. दुर्घटनाग्रस्त हो गया!
बेनोपेज

@gjvdkamp मैंने अपने उत्तर को एक समानांतर संस्करण के साथ अद्यतन किया है जो मुझे लगता है कि झूठी साझाकरण समस्या को समाप्त करता है।
jdphenix

0

आपकी संख्याओं में से कुछ पूरी तरह से बिट्स के पूर्णांक नुम्बर पर फिट होते हैं, इसलिए आप उन्हें ऊपरी स्तर की संख्या के साथ "पैक" कर सकते हैं:

for (byte lm = 0; lm < 12; lm++)
{
    ...
    t[z].l = (lm&12)>>2;
    t[z].m = lm&3;
    ...
}

बेशक, यह कोड को कम पठनीय बनाता है, लेकिन आपने एक लूप बचाया। यह हर बार किया जा सकता है जब संख्याओं में से एक दो की शक्ति होती है, जो आपके मामले में सात बार होती है।


मैं इस उत्तर के बारे में अधिक जानना चाहता हूं - क्या आप इस पर विस्तार कर सकते हैं?
बेन्पेज

देर से उत्तर देने के लिए हमें खेद है। m 0 से 3 तक जाता है, जो बाइनरी में 00 से 11 बनता है, l से 0 से 2 तक, जो 00 से 10 बनाता है, इसलिए यदि आप उन्हें अलग-अलग प्रिंट करते हैं, तो यह होगा: 00 00 00 01 00 10 00 11 11 01 00। । 10 11 आप 4 बिट्स की एक ही संख्या में एक साथ 0000 से 1011 तक जा सकते हैं, और मास्क बिट का उपयोग करके उपयुक्त बिट्स का चयन कर सकते हैं और 3 बायोमास बनाता है और एलएम के बीच (11) बी एलएम और 12 एलएम के साथ एक ही बनाता है और (1100) b तब हम "वास्तविक" संख्या के लिए दो बिट्स द्वारा स्थानांतरित करते हैं। वैसे, बस एहसास हुआ कि इस मामले में lm >> 2 करने के लिए पर्याप्त है।
फेबियन ड्यूपॉन्ट

0

यहाँ एक और उपाय है। वीएस के बाहर यह 437.5 एमएस के रूप में तेजी से चलता है जो मूल कोड (मेरे कंप्यूटर पर 593.7) से 26% तेज है:

static List<byte[]> Combinations(byte[] maxs)
{
  int length = maxs.Length;
  int count = 1; // 3981312;
  Array.ForEach(maxs, m => count *= m);
  byte[][] data = new byte[count][];
  byte[] counters = new byte[length];

  for (int r = 0; r < count; r++)
  {
    byte[] row = new byte[length];
    for (int c = 0; c < length; c++)
      row[c] = counters[c];
    data[r] = row;

    for (int i = length - 1; i >= 0; i--)
    {
      counters[i]++;
      if (counters[i] == maxs[i])
        counters[i] = 0;
      else
        break;
    }
  }

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