algorithm - ए के लिए तत्वों के सभी संयोजनों को वापस करने के लिए एल्गोरिदम




combinations (20)

आइए कहें कि अक्षरों की आपकी सरणी इस तरह दिखती है: "एबीसीडीएफएचजी"। आपके पास तीन सूचकांक हैं (i, j, k) यह इंगित करता है कि आप वर्तमान शब्द के लिए कौन से अक्षरों का उपयोग करने जा रहे हैं, आप इसके साथ शुरू करते हैं:

A B C D E F G H
^ ^ ^
i j k

सबसे पहले आप के बदलते हैं, इसलिए अगला कदम ऐसा लगता है:

A B C D E F G H
^ ^   ^
i j   k

यदि आप अंत तक पहुंचे तो आप आगे बढ़ते हैं और फिर जे और फिर के बदलते हैं।

A B C D E F G H
^   ^ ^
i   j k

A B C D E F G H
^   ^   ^
i   j   k

एक बार जब आप जी पहुंचे तो आप भी अलग-अलग होने लगते हैं।

A B C D E F G H
  ^ ^ ^
  i j k

A B C D E F G H
  ^ ^   ^
  i j   k
...

कोड में लिखा यह इस तरह कुछ दिखता है

void print_combinations(const char *string)
{
    int i, j, k;
    int len = strlen(string);

    for (i = 0; i < len - 2; i++)
    {
        for (j = i + 1; j < len - 1; j++)
        {
            for (k = j + 1; k < len; k++)
                printf("%c%c%c\n", string[i], string[j], string[k]);
        }
    }
}

मैं एक ऐसा फ़ंक्शन लिखना चाहता हूं जो अक्षरों को एक तर्क के रूप में लेता है और चयन करने के लिए उन अक्षरों की संख्या लेता है।

मान लें कि आप 8 अक्षरों की सरणी प्रदान करते हैं और उससे 3 अक्षरों का चयन करना चाहते हैं। फिर आपको यह मिलना चाहिए:

8! / ((8 - 3)! * 3!) = 56

बदले में Arrays (या शब्द) प्रत्येक 3 अक्षरों से मिलकर।


क्या मैं इस समस्या के लिए अपने रिकर्सिव पायथन समाधान पेश कर सकता हूं?

def choose_iter(elements, length):
    for i in xrange(len(elements)):
        if length == 1:
            yield (elements[i],)
        else:
            for next in choose_iter(elements[i+1:len(elements)], length-1):
                yield (elements[i],) + next
def choose(l, k):
    return list(choose_iter(l, k))

उदाहरण का उपयोग:

>>> len(list(choose_iter("abcdefgh",3)))
56

मुझे इसकी सादगी के लिए यह पसंद है।


निम्नलिखित रिकर्सिव एल्गोरिदम एक क्रमबद्ध सेट से सभी के-तत्व संयोजनों को चुनता है:

  • अपने संयोजन का पहला तत्व चुनें
  • i k-1 i तत्वों के सेट से रिक्त रूप से चुने गए के k-1 तत्वों के प्रत्येक संयोजन के साथ गठबंधन करें।

सेट में प्रत्येक i लिए उपरोक्त Iterate।

पुनरावृत्ति से बचने के लिए, आप आवश्यक तत्वों को जितना बड़ा i , उतना ही आवश्यक है। इस तरह [3,5] केवल एक बार उठाया जाएगा, [3] [5] के साथ संयुक्त, दो बार की बजाय (स्थिति समाप्त हो जाती है [5] + [3])। इस स्थिति के बिना आप संयोजनों के बजाय विविधता प्राप्त करते हैं।


पायथन में संक्षिप्त उदाहरण:

def comb(sofar, rest, n):
    if n == 0:
        print sofar
    else:
        for i in range(len(rest)):
            comb(sofar + rest[i], rest[i+1:], n-1)

>>> comb("", "abcde", 3)
abc
abd
abe
acd
ace
ade
bcd
bce
bde
cde

स्पष्टीकरण के लिए, रिकर्सिव विधि को निम्नलिखित उदाहरण के साथ वर्णित किया गया है:

उदाहरण: एबीसीडीई
3 के सभी संयोजन होंगे:

  • ए शेष से 2 के सभी संयोजनों के साथ (बीसीडीई)
  • शेष 2 से सभी संयोजनों के साथ बी (सीडीई)
  • बाकी से 2 के सभी संयोजनों के साथ सी (डीई)

मैंने इसके लिए SQL सर्वर 2005 में एक समाधान बनाया है, और इसे अपनी वेबसाइट पर पोस्ट किया है: http://www.jessemclain.com/downloads/code/sql/fn_GetMChooseNCombos.sql.htm

उपयोग दिखाने के लिए यहां एक उदाहरण दिया गया है:

SELECT * FROM dbo.fn_GetMChooseNCombos('ABCD', 2, '')

परिणाम:

Word
----
AB
AC
AD
BC
BD
CD

(6 row(s) affected)

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

function string_recurse(active, rest) {
    if (rest.length == 0) {
        console.log(active);
    } else {
        string_recurse(active + rest.charAt(0), rest.substring(1, rest.length));
        string_recurse(active, rest.substring(1, rest.length));
    }
}
string_recurse("", "abc");

आउटपुट निम्नानुसार होना चाहिए:

abc
ab
ac
a
bc
b
c

यहां आपके पास सी # में कोडित एल्गोरिदम का आलसी मूल्यांकन संस्करण है:

    static bool nextCombination(int[] num, int n, int k)
    {
        bool finished, changed;

        changed = finished = false;

        if (k > 0)
        {
            for (int i = k - 1; !finished && !changed; i--)
            {
                if (num[i] < (n - 1) - (k - 1) + i)
                {
                    num[i]++;
                    if (i < k - 1)
                    {
                        for (int j = i + 1; j < k; j++)
                        {
                            num[j] = num[j - 1] + 1;
                        }
                    }
                    changed = true;
                }
                finished = (i == 0);
            }
        }

        return changed;
    }

    static IEnumerable Combinations<T>(IEnumerable<T> elements, int k)
    {
        T[] elem = elements.ToArray();
        int size = elem.Length;

        if (k <= size)
        {
            int[] numbers = new int[k];
            for (int i = 0; i < k; i++)
            {
                numbers[i] = i;
            }

            do
            {
                yield return numbers.Select(n => elem[n]);
            }
            while (nextCombination(numbers, size, k));
        }
    }

और परीक्षण भाग:

    static void Main(string[] args)
    {
        int k = 3;
        var t = new[] { "dog", "cat", "mouse", "zebra"};

        foreach (IEnumerable<string> i in Combinations(t, k))
        {
            Console.WriteLine(string.Join(",", i));
        }
    }

उम्मीद है कि यह आपकी मदद करेगा!


यहां एक कोड है जिसे मैंने हाल ही में जावा में लिखा था, जो "आउटऑफ" तत्वों से "num" तत्वों के सभी संयोजनों की गणना करता है और देता है।

// author: Sourabh Bhat ([email protected])

public class Testing
{
    public static void main(String[] args)
    {

// Test case num = 5, outOf = 8.

        int num = 5;
        int outOf = 8;
        int[][] combinations = getCombinations(num, outOf);
        for (int i = 0; i < combinations.length; i++)
        {
            for (int j = 0; j < combinations[i].length; j++)
            {
                System.out.print(combinations[i][j] + " ");
            }
            System.out.println();
        }
    }

    private static int[][] getCombinations(int num, int outOf)
    {
        int possibilities = get_nCr(outOf, num);
        int[][] combinations = new int[possibilities][num];
        int arrayPointer = 0;

        int[] counter = new int[num];

        for (int i = 0; i < num; i++)
        {
            counter[i] = i;
        }
        breakLoop: while (true)
        {
            // Initializing part
            for (int i = 1; i < num; i++)
            {
                if (counter[i] >= outOf - (num - 1 - i))
                    counter[i] = counter[i - 1] + 1;
            }

            // Testing part
            for (int i = 0; i < num; i++)
            {
                if (counter[i] < outOf)
                {
                    continue;
                } else
                {
                    break breakLoop;
                }
            }

            // Innermost part
            combinations[arrayPointer] = counter.clone();
            arrayPointer++;

            // Incrementing part
            counter[num - 1]++;
            for (int i = num - 1; i >= 1; i--)
            {
                if (counter[i] >= outOf - (num - 1 - i))
                    counter[i - 1]++;
            }
        }

        return combinations;
    }

    private static int get_nCr(int n, int r)
    {
        if(r > n)
        {
            throw new ArithmeticException("r is greater then n");
        }
        long numerator = 1;
        long denominator = 1;
        for (int i = n; i >= r + 1; i--)
        {
            numerator *= i;
        }
        for (int i = 2; i <= n - r; i++)
        {
            denominator *= i;
        }

        return (int) (numerator / denominator);
    }
}

लघु जावा समाधान:

import java.util.Arrays;

public class Combination {
    public static void main(String[] args){
        String[] arr = {"A","B","C","D","E","F"};
        combinations2(arr, 3, 0, new String[3]);
    }

    static void combinations2(String[] arr, int len, int startPosition, String[] result){
        if (len == 0){
            System.out.println(Arrays.toString(result));
            return;
        }       
        for (int i = startPosition; i <= arr.length-len; i++){
            result[result.length - len] = arr[i];
            combinations2(arr, len-1, i+1, result);
        }
    }       
}

परिणाम होगा

[A, B, C]
[A, B, D]
[A, B, E]
[A, B, F]
[A, C, D]
[A, C, E]
[A, C, F]
[A, D, E]
[A, D, F]
[A, E, F]
[B, C, D]
[B, C, E]
[B, C, F]
[B, D, E]
[B, D, F]
[B, E, F]
[C, D, E]
[C, D, F]
[C, E, F]
[D, E, F]

संयोजन सूचकांक की आलसी पीढ़ी के साथ एक और सी # संस्करण। यह संस्करण सभी मानों की सूची और वर्तमान संयोजन के मानों के बीच मैपिंग को परिभाषित करने के लिए इंडेक्स की एक सरणी को बनाए रखता है, यानी पूरे रनटाइम के दौरान लगातार ओ (के) अतिरिक्त स्थान का उपयोग करता है। कोड ओ (के) समय में पहले संयोजन सहित व्यक्तिगत संयोजन उत्पन्न करता है।

public static IEnumerable<T[]> Combinations<T>(this T[] values, int k)
{
    if (k < 0 || values.Length < k)
        yield break; // invalid parameters, no combinations possible

    // generate the initial combination indices
    var combIndices = new int[k];
    for (var i = 0; i < k; i++)
    {
        combIndices[i] = i;
    }

    while (true)
    {
        // return next combination
        var combination = new T[k];
        for (var i = 0; i < k; i++)
        {
            combination[i] = values[combIndices[i]];
        }
        yield return combination;

        // find first index to update
        var indexToUpdate = k - 1;
        while (indexToUpdate >= 0 && combIndices[indexToUpdate] >= values.Length - k + indexToUpdate)
        {
            indexToUpdate--;
        }

        if (indexToUpdate < 0)
            yield break; // done

        // update combination indices
        for (var combIndex = combIndices[indexToUpdate] + 1; indexToUpdate < k; indexToUpdate++, combIndex++)
        {
            combIndices[indexToUpdate] = combIndex;
        }
    }
}

टेस्ट कोड:

foreach (var combination in new[] {'a', 'b', 'c', 'd', 'e'}.Combinations(3))
{
    System.Console.WriteLine(String.Join(" ", combination));
}

आउटपुट:

a b c
a b d
a b e
a c d
a c e
a d e
b c d
b c e
b d e
c d e

सी # में:

public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
  return k == 0 ? new[] { new T[0] } :
    elements.SelectMany((e, i) =>
      elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] {e}).Concat(c)));
}

उपयोग:

var result = Combinations(new[] { 1, 2, 3, 4, 5 }, 3);

परिणाम:

123
124
125
134
135
145
234
235
245
345

सी ++ में निम्नलिखित दिनचर्या सीमा [प्रथम, अंतिम) के बीच लंबाई दूरी (पहले, के) के सभी संयोजनों का उत्पादन करेगी:

#include <algorithm>

template <typename Iterator>
bool next_combination(const Iterator first, Iterator k, const Iterator last)
{
   /* Credits: Mark Nelson http://marknelson.us */
   if ((first == last) || (first == k) || (last == k))
      return false;
   Iterator i1 = first;
   Iterator i2 = last;
   ++i1;
   if (last == i1)
      return false;
   i1 = last;
   --i1;
   i1 = k;
   --i2;
   while (first != i1)
   {
      if (*--i1 < *i2)
      {
         Iterator j = k;
         while (!(*i1 < *j)) ++j;
         std::iter_swap(i1,j);
         ++i1;
         ++j;
         i2 = k;
         std::rotate(i1,j,last);
         while (last != j)
         {
            ++j;
            ++i2;
         }
         std::rotate(k,i2,last);
         return true;
      }
   }
   std::rotate(first,k,last);
   return false;
}

इसका इस्तेमाल इस तरह किया जा सकता है:

#include <string>
#include <iostream>

int main()
{
    std::string s = "12345";
    std::size_t comb_size = 3;
    do
    {
        std::cout << std::string(s.begin(), s.begin() + comb_size) << std::endl;
    } while (next_combination(s.begin(), s.begin() + comb_size, s.end()));

    return 0;
}

यह निम्नलिखित प्रिंट करेगा:

123
124
125
134
135
145
234
235
245
345

99 स्कैला समस्याओं पर वर्णित अनुसार, स्कैला में एक सुरुचिपूर्ण, सामान्य कार्यान्वयन है।

object P26 {
  def flatMapSublists[A,B](ls: List[A])(f: (List[A]) => List[B]): List[B] = 
    ls match {
      case Nil => Nil
      case [email protected](_ :: tail) => f(sublist) ::: flatMapSublists(tail)(f)
    }

  def combinations[A](n: Int, ls: List[A]): List[List[A]] =
    if (n == 0) List(Nil)
    else flatMapSublists(ls) { sl =>
      combinations(n - 1, sl.tail) map {sl.head :: _}
    }
}

आर्ट ऑफ कम्प्यूटर प्रोग्रामिंग वॉल्यूम 4: फास्किकल 3 में इनमें से एक टन है जो आपके वर्णन की तुलना में आपकी विशेष स्थिति को बेहतर तरीके से फिट कर सकता है।

ग्रे कोड

एक मुद्दा जो आप पार करेंगे, निश्चित रूप से स्मृति और बहुत तेज़ी से है, आपको अपने सेट में 20 तत्वों की समस्या होगी - 20 सी 3 = 1140. और यदि आप सेट पर फिर से शुरू करना चाहते हैं तो संशोधित ग्रे का उपयोग करना सबसे अच्छा है कोड एल्गोरिदम इसलिए आप सभी को स्मृति में नहीं रख रहे हैं। ये पिछले से अगला संयोजन उत्पन्न करते हैं और पुनरावृत्ति से बचते हैं। इनमें से कई अलग-अलग उपयोगों के लिए हैं। क्या हम लगातार संयोजनों के बीच मतभेदों को अधिकतम करना चाहते हैं? छोटा करना? एट cetera।

ग्रे कोड का वर्णन करने वाले कुछ मूल कागजात:

  1. कुछ हैमिल्टन पथ और एक न्यूनतम परिवर्तन एल्गोरिदम
  2. निकट इंटरचेंज संयोजन जनरेशन एल्गोरिदम

विषय को कवर करने वाले कुछ अन्य कागजात यहां दिए गए हैं:

  1. ईड्स, हिकी का एक कुशल कार्यान्वयन, निकट इंटरचेंज संयोजन जनरेशन एल्गोरिदम पढ़ें (पीडीएफ, पास्कल में कोड के साथ)
  2. संयोजन जनरेटर
  3. कॉम्बिनेटोरियल ग्रे कोड्स का सर्वेक्षण (पोस्टस्क्रिप्ट)
  4. ग्रे कोड के लिए एक एल्गोरिदम

चेस ट्विडल (एल्गोरिदम)

फिलिप जे चेस, ' एल्गोरिदम 382: एम ऑब्जेक्ट्स ऑफ एम ऑब्जेक्ट्स ' (1 9 70)

सी में एल्गोरिदम ...

लेक्सिकोोग्राफिक ऑर्डर में संयोजन का सूचकांक (बक्ल्स एल्गोरिदम 515)

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

तो, हमारे पास एक सेट {1,2,3,4,5,6} है ... और हम तीन तत्व चाहते हैं। मान लें कि {1,2,3} हम कह सकते हैं कि तत्वों के बीच का अंतर एक और क्रम में और न्यूनतम है। {1,2,4} में एक बदलाव है, और लेक्सिकोग्राफिक संख्या 2 है। इसलिए अंतिम स्थान में 'परिवर्तन' की संख्या लेक्सिकोोग्राफिक क्रम में एक बदलाव के लिए जिम्मेदार है। दूसरी जगह, एक परिवर्तन के साथ {1,3,4} में एक बदलाव है, लेकिन दूसरे स्थान पर होने के बाद से अधिक परिवर्तन के लिए खाते हैं (मूल सेट में तत्वों की संख्या के आनुपातिक)।

जिस पद्धति का मैंने वर्णन किया है वह एक निर्णायक है, जैसा कि लगता है, सेट से इंडेक्स तक, हमें रिवर्स करने की आवश्यकता है - जो बहुत अधिक कठिन है। इस तरह Buckles समस्या हल करता है। मैंने मामूली परिवर्तनों के साथ उन्हें गणना करने के लिए कुछ सी लिखा - मैंने सेट का प्रतिनिधित्व करने के लिए संख्याओं की बजाय सेट की अनुक्रमणिका का उपयोग किया, इसलिए हम हमेशा 0 से काम कर रहे हैं ... n। ध्यान दें:

  1. चूंकि संयोजन अनियंत्रित हैं, {1,3,2} = {1,2,3} - हम उन्हें लेक्सिकोोग्राफिक होने का आदेश देते हैं।
  2. पहले अंतर के लिए सेट शुरू करने के लिए इस विधि में एक अंतर्निहित 0 है।

लेक्सिकोोग्राफिक ऑर्डर (मैककैफ्री) में संयोजन का सूचकांक

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

सेट जो अधिकतम करता है , कहा पे

उदाहरण के लिए: 27 = C(6,4) + C(5,3) + C(2,2) + C(1,1) । तो, चार चीजों का 27 वां शब्दावली संयोजन है: {1,2,5,6}, वे जो भी सेट देखना चाहते हैं, वे इंडेक्स हैं। नीचे उदाहरण (ओकैमल), पाठक को छोड़ने की आवश्यकता है, पाठक को छोड़ दिया:

(* this will find the [x] combination of a [set] list when taking [k] elements *)
let combination_maccaffery set k x =
    (* maximize function -- maximize a that is aCb              *)
    (* return largest c where c < i and choose(c,i) <= z        *)
    let rec maximize a b x =
        if (choose a b ) <= x then a else maximize (a-1) b x
    in
    let rec iterate n x i = match i with
        | 0 -> []
        | i ->
            let max = maximize n i x in
            max :: iterate n (x - (choose max i)) (i-1)
    in
    if x < 0 then failwith "errors" else
    let idxs =  iterate (List.length set) x k in
    List.map (List.nth set) (List.sort (-) idxs)

A concise Javascript solution:

Array.prototype.combine=function combine(k){    
    var toCombine=this;
    var last;
    function combi(n,comb){             
        var combs=[];
        for ( var x=0,y=comb.length;x<y;x++){
            for ( var l=0,m=toCombine.length;l<m;l++){      
                combs.push(comb[x]+toCombine[l]);           
            }
        }
        if (n<k-1){
            n++;
            combi(n,combs);
        } else{last=combs;}
    }
    combi(1,toCombine);
    return last;
}
// Example:
// var toCombine=['a','b','c'];
// var results=toCombine.combine(4);

Here is a method which gives you all combinations of specified size from a random length string. Similar to quinmars' solution, but works for varied input and k.

The code can be changed to wrap around, ie 'dab' from input 'abcd' wk=3.

public void run(String data, int howMany){
    choose(data, howMany, new StringBuffer(), 0);
}


//n choose k
private void choose(String data, int k, StringBuffer result, int startIndex){
    if (result.length()==k){
        System.out.println(result.toString());
        return;
    }

    for (int i=startIndex; i<data.length(); i++){
        result.append(data.charAt(i));
        choose(data,k,result, i+1);
        result.setLength(result.length()-1);
    }
}

Output for "abcde":

abc abd abe acd ace ade bcd bce bde cde


I have written a class to handle common functions for working with the binomial coefficient, which is the type of problem that your problem falls under. It performs the following tasks:

  1. Outputs all the K-indexes in a nice format for any N choose K to a file. The K-indexes can be substituted with more descriptive strings or letters. This method makes solving this type of problem quite trivial.

  2. Converts the K-indexes to the proper index of an entry in the sorted binomial coefficient table. This technique is much faster than older published techniques that rely on iteration. It does this by using a mathematical property inherent in Pascal's Triangle. My paper talks about this. I believe I am the first to discover and publish this technique, but I could be wrong.

  3. Converts the index in a sorted binomial coefficient table to the corresponding K-indexes.

  4. Uses Mark Dominus method to calculate the binomial coefficient, which is much less likely to overflow and works with larger numbers.

  5. The class is written in .NET C# and provides a way to manage the objects related to the problem (if any) by using a generic list. The constructor of this class takes a bool value called InitTable that when true will create a generic list to hold the objects to be managed. If this value is false, then it will not create the table. The table does not need to be created in order to perform the 4 above methods. Accessor methods are provided to access the table.

  6. There is an associated test class which shows how to use the class and its methods. It has been extensively tested with 2 cases and there are no known bugs.

To read about this class and download the code, see Tablizing The Binomial Coeffieicent .

It should not be hard to convert this class to C++.


Lets say your array of letters looks like this: "ABCDEFGH". You have three indices (i, j, k) indicating which letters you are going to use for the current word, You start with:

A B C D E F G H
^ ^ ^
i j k

First you vary k, so the next step looks like that:

A B C D E F G H
^ ^   ^
i j   k

If you reached the end you go on and vary j and then k again.

A B C D E F G H
^   ^ ^
i   j k

A B C D E F G H
^   ^   ^
i   j   k

Once you j reached G you start also to vary i.

A B C D E F G H
  ^ ^ ^
  i j k

A B C D E F G H
  ^ ^   ^
  i j   k
...
function initializePointers($cnt) {
    $pointers = [];

    for($i=0; $i<$cnt; $i++) {
        $pointers[] = $i;
    }

    return $pointers;     
}

function incrementPointers(&$pointers, &$arrLength) {
    for($i=0; $i<count($pointers); $i++) {
        $currentPointerIndex = count($pointers) - $i - 1;
        $currentPointer = $pointers[$currentPointerIndex];

        if($currentPointer < $arrLength - $i - 1) {
           ++$pointers[$currentPointerIndex];

           for($j=1; ($currentPointerIndex+$j)<count($pointers); $j++) {
                $pointers[$currentPointerIndex+$j] = $pointers[$currentPointerIndex]+$j;
           }

           return true;
        }
    }

    return false;
}

function getDataByPointers(&$arr, &$pointers) {
    $data = [];

    for($i=0; $i<count($pointers); $i++) {
        $data[] = $arr[$pointers[$i]];
    }

    return $data;
}

function getCombinations($arr, $cnt)
{
    $len = count($arr);
    $result = [];
    $pointers = initializePointers($cnt);

    do {
        $result[] = getDataByPointers($arr, $pointers);
    } while(incrementPointers($pointers, count($arr)));

    return $result;
}

$result = getCombinations([0, 1, 2, 3, 4, 5], 3);
print_r($result);

Based on https://.com/a/127898/2628125 , but more abstract, for any size of pointers.


static IEnumerable<string> Combinations(List<string> characters, int length)
{
    for (int i = 0; i < characters.Count; i++)
    {
        // only want 1 character, just return this one
        if (length == 1)
            yield return characters[i];

        // want more than one character, return this one plus all combinations one shorter
        // only use characters after the current one for the rest of the combinations
        else
            foreach (string next in Combinations(characters.GetRange(i + 1, characters.Count - (i + 1)), length - 1))
                yield return characters[i] + next;
    }
}

Array.prototype.combs = function(num) {

    var str = this,
        length = str.length,
        of = Math.pow(2, length) - 1,
        out, combinations = [];

    while(of) {

        out = [];

        for(var i = 0, y; i < length; i++) {

            y = (1 << i);

            if(y & of && (y !== of))
                out.push(str[i]);

        }

        if (out.length >= num) {
           combinations.push(out);
        }

        of--;
    }

    return combinations;
}






combinations