2011-01-13 21 views
18

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

मुझे यकीन नहीं है कि यह संभव है, लेकिन मैं निम्नलिखित करना चाहता हूं। किसी फ़ंक्शन के लिए, संख्याओं की एक सरणी पास हो जाती है। प्रत्येक संख्या, उदाहरण के लिए, पाश के लिए एक की ऊपरी सीमा है अगर सरणी [2, 3, 5] है, निम्न कोड निष्पादित किया जाना चाहिए:

for(var a = 0; a < 2; a++) { 
    for(var b = 0; b < 3; b++) { 
      for(var c = 0; c < 5; c++) { 
       doSomething([a, b, c]); 
      } 
    } 
} 

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

कौन सी तकनीक यहां उपयुक्त हो सकती है?

+2

तो आप बस कुछ समारोह कई बार जो एक से पारित कर दिया-इन सरणी में संख्याओं का गुणनफल के बराबर होता है कॉल करना चाहते हैं? – Sean

+0

नहीं, मुझे खेद है। मुझे लूप (ए, बी और सी यहां) के चर के लिए भी आवश्यकता होगी। – pimvdb

उत्तर

17

Recursion इस समस्या को बड़े करीने से हल कर सकते हैं:

function callManyTimes(maxIndices, func) { 
    doCallManyTimes(maxIndices, func, [], 0); 
} 

function doCallManyTimes(maxIndices, func, args, index) { 
    if (maxIndices.length == 0) { 
     func(args); 
    } else { 
     var rest = maxIndices.slice(1); 
     for (args[index] = 0; args[index] < maxIndices[0]; ++args[index]) { 
      doCallManyTimes(rest, func, args, index + 1); 
     } 
    } 
} 

इसे इस तरह कॉल करें:

callManyTimes([2,3,5], doSomething); 
+0

आपका समाधान भी एक आकर्षण की तरह काम करता है। वास्तव में आपका सबसे साफ है, और मेरे लिए समझना आसान है। बहुत बहुत धन्यवाद – pimvdb

+0

ग्रेट सॉल्यूशन, आपका सबसे तेज़ प्रस्ताव भी है।मेरा (अवैज्ञानिक) परीक्षण दिखाता है कि यदि हम देशी नेस्टेड लूप को बेंचमार्क 'एक्स' के रूप में लेते हैं, तो: शॉन: '4 एक्स', गुफा:' 8 एक्स', माइक सैमुअल: '15 एक्स', पॉइंटी:' 28 एक्स'। – serg

3

सीमा सरणी के समान लंबाई के साथ काउंटरों की एक सरणी सेट करें। एक लूप का प्रयोग करें, और प्रत्येक पुनरावृत्ति में अंतिम आइटम को बढ़ाएं। जब यह इसकी सीमा तक पहुंच जाता है तो आप इसे पुनरारंभ करते हैं और अगले आइटम को बढ़ाते हैं।

function loop(limits) { 
    var cnt = new Array(limits.length); 
    for (var i = 0; i < cnt.length; i++) cnt[i] = 0; 
    var pos; 
    do { 
    doSomething(cnt); 
    pos = cnt.length - 1; 
    cnt[pos]++; 
    while (pos >= 0 && cnt[pos] >= limits[pos]) { 
     cnt[pos] = 0; 
     pos--; 
     if (pos >= 0) cnt[pos]++; 
    } 
    } while (pos >= 0); 
} 
1

के तीन छोरों कर के बीच कोई अंतर नहीं है 2, 3, 5, और में से एक पाश 30 (2 * 3 * 5)।

function doLots (howMany, what) { 
    var amount = 0; 

    // Aggregate amount 
    for (var i=0; i<howMany.length;i++) { 
     amount *= howMany[i]; 
    }; 

    // Execute that many times.  
    while(i--) { 
     what(); 
    }; 
} 

उपयोग:

doLots([2,3,5], doSomething); 
+1

मुझे वास्तव में खेद है, लेकिन मुझे काउंटर वैरिएबल के मूल्यों की भी आवश्यकता होगी। हालांकि मैं आपके समाधान से प्यार करता हूं, वह जानकारी गुम हो जाती है। – pimvdb

+0

आपने मुझे इसे हराया। इसके अलावा, आपको किस तरह की जानकारी की आवश्यकता है? क्या आप बस मूल सरणी में सभी पूर्णांक धारण कर सकते हैं? या क्या आपको उन्हें किसी अलग तरीके से रखने की ज़रूरत है? – user535617

+0

मैं एक सामान्य बहुआयामी सरणी फ़ंक्शन बनाने की कोशिश कर रहा हूं, इसलिए मुझे एक मान के साथ इंडेक्स के प्रत्येक संयोजन को भरने की आवश्यकता होगी। इसलिए लूप के लिए घोंसला। लूप के लिए एक इंडेक्स खो जाता है और सिर्फ एक इंडेक्स (0 - 30 यहां) – pimvdb

2

नेस्टेड for छोरों के मामले में सोच करने के बजाय, पुनरावर्ती क्रिया आमंत्रण के बारे में सोचते हैं।

function forEachCounter(counters, fn) { 
    function impl(counters, curCount) { 
    if (counters.length === 0) 
     fn(curCount); 
    else { 
     var limit = counters[0]; 
     curCount.push(0); 
     for (var i = 0; i < limit; ++i) { 
     curCount[curCount.length - 1] = i; 
     impl(counters.slice(1), curCount); 
     } 
     curCount.length--; 
    } 
    } 
    impl(counters, []); 
} 

आप एक तर्क यह है कि अपनी सूची के साथ समारोह फोन चाहते हैं: कुछ इस तरह लग सकता है कि

if the list of counters is empty 
    then "doSomething()" 
else 
    for (counter = 0 to first counter limit in the list) 
     recurse with the tail of the list 

: अपनी यात्रा करने के लिए, आप निम्नलिखित निर्णय (छद्म कोड) बनाने चाहते हैं गिनती सीमाओं का, और एक तर्क जो प्रत्येक प्रभावी गिनती सरणी ("doSomething" भाग) के लिए निष्पादित करने के लिए आपका कार्य है। उपरोक्त मुख्य कार्य एक आंतरिक कार्य में सभी वास्तविक कार्य करता है। उस आंतरिक कार्य में, पहला तर्क काउंटर सीमा सूची है, जिसे "whittled" किया जाएगा क्योंकि फ़ंक्शन को पुनरावर्ती कहा जाता है। दूसरे तर्क का उपयोग काउंटर वैल्यू के वर्तमान सेट को पकड़ने के लिए किया जाता है, ताकि "डूसमिंग" यह जान सके कि यह वास्तविक गणनाओं की एक विशेष सूची के अनुरूप एक पुनरावृत्ति पर है।

समारोह कॉलिंग इस प्रकार दिखाई देगा:

forEachCounter([4, 2, 5], function(c) { /* something */ }); 
+1

देता है जो दिलचस्प लगता है, बहुत बहुत धन्यवाद। – pimvdb

1

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

var product = 0; 
for(var i = 0; i < array.length; i++){ 
    product *= array[i]; 
} 

for(var i = 0; i < product; i++){ 
    doSomething(); 
} 

वैकल्पिक रूप से:

for(var i = 0; i < array.length; i++){ 
    for(var j = 0; j < array[i]; j++){ 
     doSomething(); 
    } 
} 
7

रिकर्सन यहां अधिक है। एक बहुत तेजी से समाधान: 2 एक्स 0: 3 एक्स 0:

function allPossibleCombinations(lengths, fn) { 
 
    var n = lengths.length; 
 

 
    var indices = []; 
 
    for (var i = n; --i >= 0;) { 
 
    if (lengths[i] === 0) { return; } 
 
    if (lengths[i] !== (lengths[i] & 0x7ffffffff)) { throw new Error(); } 
 
    indices[i] = 0; 
 
    } 
 

 
    while (true) { 
 
    fn.apply(null, indices); 
 
    // Increment indices. 
 
    ++indices[n - 1]; 
 
    for (var j = n; --j >= 0 && indices[j] === lengths[j];) { 
 
     if (j === 0) { return; } 
 
     indices[j] = 0; 
 
     ++indices[j - 1]; 
 
    } 
 
    } 
 
} 
 

 
allPossibleCombinations([3, 2, 2], function(a, b, c) { console.log(a + ',' + b + ',' + c); })

+0

यह भी एक सरल तरीका है। क्या आप संभवतः समझा सकते हैं कि आप 0x7fffffff के साथ क्या कर रहे हैं? – pimvdb

+0

@pimvdb, यह सुनिश्चित करना कि लंबाई गैर-ऋणात्मक पूर्णांक हैं ताकि 'इंडेक्स [जे] === लंबाई [जे]' नीचे की जांच करने का मौका हो। –

+0

मैंने इसे कम करने के कुछ तरीकों को देखा और इसे मेरे उपयोग के मामले के लिए अधिक बहुमुखी बना दिया: https://stackoverflow.com/a/44753698/552067 धन्यवाद माइक! –

0

आप कार्तीय उत्पाद 0 के सभी तत्वों की गणना करने में लालची एल्गोरिथ्म का उपयोग कर सकते 5। यह एल्गोरिदम नीचे मेरे फ़ंक्शन greedy_backward द्वारा किया जाता है। मैं जावास्क्रिप्ट में एक विशेषज्ञ नहीं हूं और शायद इस समारोह में सुधार किया जा सकता है।

function greedy_backward(sizes, n) { 
    for (var G = [1], i = 0; i<sizes.length; i++) G[i+1] = G[i] * sizes[i]; 
    if (n>=_.last(G)) throw new Error("n must be <" + _.last(G)); 
    for (i = 0; i<sizes.length; i++) if (sizes[i]!=parseInt(sizes[i]) || sizes[i]<1){ throw new Error("sizes must be a vector of integers be >1"); }; 
    for (var epsilon=[], i=0; i < sizes.length; i++) epsilon[i]=0; 
    while(n > 0){ 
    var k = _.findIndex(G, function(x){ return n < x; }) - 1; 
    var e = (n/G[k])>>0; 
    epsilon[k] = e; 
    n = n-e*G[k]; 
    } 
    return epsilon; 
} 

यह विरोधी कोषगत क्रम में कार्तीय उत्पाद के तत्वों विश्लेषण करता है (यदि आप doSomething उदाहरण में पूर्ण गणन देखेंगे):

~ var sizes = [2, 3, 5]; 
~ greedy_backward(sizes,0); 
0,0,0 
~ greedy_backward(sizes,1); 
1,0,0 
~ greedy_backward(sizes,2); 
0,1,0 
~ greedy_backward(sizes,3); 
1,1,0 
~ greedy_backward(sizes,4); 
0,2,0 
~ greedy_backward(sizes,5); 
1,2,0 

यह द्विआधारी प्रतिनिधित्व के सामान्यीकरण है (मामला जब sizes=[2,2,2,...])।

उदाहरण:

~ function doSomething(v){ 
    for (var message = v[0], i = 1; i<v.length; i++) message = message + '-' + v[i].toString(); 
    console.log(message); 
    } 
~ doSomething(["a","b","c"]) 
a-b-c 
~ for (var max = [1], i = 0; i<sizes.length; i++) max = max * sizes[i]; 
30 
~ for(i=0; i<max; i++){ 
    doSomething(greedy_backward(sizes,i)); 
    } 
0-0-0 
1-0-0 
0-1-0 
1-1-0 
0-2-0 
1-2-0 
0-0-1 
1-0-1 
0-1-1 
1-1-1 
0-2-1 
1-2-1 
0-0-2 
1-0-2 
0-1-2 
1-1-2 
0-2-2 
1-2-2 
0-0-3 
1-0-3 
0-1-3 
1-1-3 
0-2-3 
1-2-3 
0-0-4 
1-0-4 
0-1-4 
1-1-4 
0-2-4 
1-2-4 

यदि आवश्यक हो, रिवर्स आपरेशन सरल है:

function greedy_forward(sizes, epsilon) { 
    if (sizes.length!=epsilon.length) throw new Error("sizes and epsilon must have the same length"); 
    for (i = 0; i<sizes.length; i++) if (epsilon[i] <0 || epsilon[i] >= sizes[i]){ throw new Error("condition `0 <= epsilon[i] < sizes[i]` not fulfilled for all i"); }; 
    for (var G = [1], i = 0; i<sizes.length-1; i++) G[i+1] = G[i] * sizes[i]; 
    for (var n = 0, i = 0; i<sizes.length; i++) n += G[i] * epsilon[i]; 
    return n; 
} 

उदाहरण:

~ epsilon = greedy_backward(sizes, 29) 
1,2,4 
~ greedy_forward(sizes, epsilon) 
29 
0

यह गैर पुनरावर्ती solution by Mike Samuel को सरल बनाने पर मेरे प्रयास है। मैं प्रत्येक पूर्णांक तर्क के लिए एक सीमा (केवल अधिकतम नहीं) सेट करने की क्षमता भी जोड़ता हूं।

function everyPermutation(args, fn) { 
 
    var indices = args.map(a => a.min); 
 

 
    for (var j = args.length; j >= 0;) { 
 
     fn.apply(null, indices); 
 

 
     // go through indices from right to left setting them to 0 
 
     for (j = args.length; j--;) { 
 
      // until we find the last index not at max which we increment 
 
      if (indices[j] < args[j].max) { 
 
       ++indices[j]; 
 
       break; 
 
      } 
 
      indices[j] = args[j].min; 
 
     } 
 
    } 
 
} 
 

 
everyPermutation([ 
 
    {min:4, max:6}, 
 
    {min:2, max:3}, 
 
    {min:0, max:1} 
 
], function(a, b, c) { 
 
    console.log(a + ',' + b + ',' + c); 
 
});

संबंधित मुद्दे