Алгоритм возврата всех комбинаций k элементов из n

Ниже метод вернет вам значение от 10000000000 до 9999999999

long min = 1000000000L
long max = 9999999999L    

public static long getRandomNumber(long min, long max){

    Random random = new Random();         
    return random.nextLong() % (max - min) + max;

}
545
задан 2 revs, 2 users 94% 13 December 2011 в 20:21
поделиться

15 ответов

Искусство Объема Программирования 4: Гроздь 3 имеет тонну их, которые могли бы соответствовать Вашей конкретной ситуации лучше, чем, как я описываю.

Коды Грея

проблемой, с которой Вы столкнетесь, является, конечно, память и довольно быстро, у Вас будут проблемы 20 элементами в Вашем наборе - <глоток> 20 C3 = 1140. И если Вы хотите выполнить итерации по набору, лучше использовать измененный алгоритм кода Грея, таким образом, Вы не держите всех их в памяти. Они генерируют следующую комбинацию от предыдущего и избегают повторений. Существуют многие из них для различного использования. Мы хотим максимизировать различия между последовательными комбинациями? минимизировать? и так далее.

Некоторые исходные бумаги, описывающие коды Грея:

  1. Некоторые Hamilton Пути и Минимальный Алгоритм Изменения
  2. Смежный Алгоритм Поколения Комбинации Обмена

Вот некоторые другие бумаги, затрагивающие тему:

  1. Эффективное внедрение Eade, Пятна, Чтения Смежный Алгоритм Поколения Комбинации Обмена (PDF, с кодом в Паскале)
  2. Генераторы Комбинации
  3. Обзор Комбинаторных Кодов Грея (PostScript)
  4. Алгоритм для Кодов Грея

Преследование Вертят (алгоритм)

Phillip J Преследование, ' Алгоритм 382: Комбинации M из Объектов N ' (1970)

алгоритм в C...

Индекс Комбинаций в Лексикографическом Порядке (Алгоритм Застежек 515)

можно также сослаться на комбинацию ее индексом (в лексикографическом порядке). Понимание, что индекс должен быть некоторым количеством изменения справа налево на основе индекса, мы можем создать что-то, что должно восстановить комбинацию.

Так, у нас есть набор {1,2,3,4,5,6}..., и мы хотим три элемента. Скажем, {1,2,3} мы можем сказать, что различие между элементами один и в порядке и минимально. {1,2,4} имеет одно изменение и лексикографически номер 2. Таким образом, количество 'изменений' в последнем месте составляет одно изменение в лексикографическом упорядочивании. Второе место, с одним изменением {1,3,4} имеет одно изменение, но составляет больше изменения, так как это во-вторых (пропорционально числу элементов в исходном наборе).

метод, который я описал, является разрушением, как это кажется от набора до индекса, мы должны сделать реверс †“, который намного более хитер. Это - то, как Застежки решают проблему. Я записал [приблизительно 1 116] C для вычисления их с незначительными изменениями †“, я использовал индекс наборов, а не диапазона числа для представления набора, таким образом, мы всегда работаем от 0... n.Примечание:

  1. , Так как комбинации не заказаны, {1,3,2} = {1,2,3} - мы приказываем, чтобы они были лексикографическими.
  2. Этот метод имеет неявный 0 для запуска набора для первого различия.

Индекс Комбинаций в Лексикографическом Порядке (McCaffrey)

существует иначе : его понятие легче схватить и программировать, но это без оптимизации Застежек. К счастью, это также не производит дублирующиеся комбинации:

набор x_k...x_1 in N , который максимизирует i = C(x_1,k) + C(x_2,k-1) + ... + C(x_k,1) , где C(n,r) = {n choose r} .

Для примера: 27 = C(6,4) + C(5,3) + C(2,2) + C(1,1). Так, 27-я лексикографическая комбинация четырех вещей: {1,2,5,6}, те - индексы любого набора, на который Вы хотите посмотреть. Пример ниже (OCaml), требует choose функция, оставленная читателю:

(* 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)

А маленький и простой итератор комбинаций

следующие два алгоритма предоставлены для дидактических целей. Они реализуют итератор и (более общее) папка полные комбинации. Они максимально быстро, имея сложность O (<глоток> n C k). Потребление памяти связывается k.

Мы запустим с итератора, который назовет пользователя предоставленным функции для каждой комбинации

let iter_combs n k f =
  let rec iter v s j =
    if j = k then f v
    else for i = s to n - 1 do iter (i::v) (i+1) (j+1) done in
  iter [] 0 0

А, более общая версия назовет пользователя предоставленным функции наряду с переменной состояния, начинающей с начального состояния. Так как мы должны передать состояние между различными состояниями, мы не будем использовать для цикла, но вместо этого, использовать рекурсию,

let fold_combs n k f x =
  let rec loop i s c x =
    if i < n then
      loop (i+1) s c @@
      let c = i::c and s = s + 1 and i = i + 1 in
      if s < k then loop i s c x else f c x
    else x in
  loop 0 0 [] x
398
ответ дан 20 revs, 12 users 76% 13 December 2011 в 20:21
поделиться

Если можно использовать синтаксис SQL - говорят, если Вы используете LINQ для доступа к полям структуры или массива, или непосредственно получаете доступ к базе данных, которая имеет таблицу под названием "Алфавит" со всего одним символьным полем "Letter", можно адаптировать следующий код:

SELECT A.Letter, B.Letter, C.Letter
FROM Alphabet AS A, Alphabet AS B, Alphabet AS C
WHERE A.Letter<>B.Letter AND A.Letter<>C.Letter AND B.Letter<>C.Letter
AND A.Letter<B.Letter AND B.Letter<C.Letter

Это возвратит все комбинации 3 букв, несмотря на это, сколько букв Вы имеете в таблице "Alphabet" (это может быть 3, 8, 10, 27, и т.д.).

, Если то, что Вы хотите, является всеми перестановками, а не комбинациями (т.е. Вы хотите, чтобы "ACB" и "ABC" рассчитали как отличающиеся, а не появились только однажды), просто удаляют последнюю строку (И один), и это сделано.

Постредактирование: После перечитывания вопроса я понимаю то, что необходимо, общие алгоритм, не только определенный для случая выбора 3 объектов. Ответ Adam Hughes является полным, к сожалению, я (еще) не могу проголосовать за него. Этот простой ответ, но работы только для того, когда Вы хотите точно 3 объекта.

9
ответ дан 3 revs, 2 users 91% 13 December 2011 в 20:21
поделиться
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;
    }
}
20
ответ дан 2 revs, 2 users 94% 13 December 2011 в 20:21
поделиться

Следующий рекурсивный алгоритм выбирает все комбинации k-элемента от упорядоченного множества:

  • выбирают первый элемент i из Вашей комбинации
  • объединение i с каждой из комбинаций k-1 элементы, выбранные рекурсивно из набора элементов, больше, чем i.

Выполняют итерации вышеупомянутого для каждого i в наборе.

важно, чтобы Вы выбрали остальную часть элементов как больше, чем i, для предотвращения повторения. Этот путь [3,5] будет выбран только однажды, как [3] объединенный с [5], вместо дважды (условие устраняет [5] + [3]). Без этого условия Вы получаете изменения вместо комбинаций.

52
ответ дан Rafał Dowgird 13 December 2011 в 20:21
поделиться

Позволяет говорят, что Ваш массив букв похож на это: "ABCDEFGH". У Вас есть три индекса (я, j, k) указание, которое обозначает буквами Вас, собираются использовать для текущего слова, Вы запускаете с:

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

Первый Вы варьируетесь k, таким образом, следующий шаг похож на это:

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

при достижении конца Вы продолжаете и варьируетесь j и затем k снова.

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

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

Однажды Вы j достигнутый G Вы начинаете также варьироваться я.

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]);
        }
    }
}
61
ответ дан 4 revs, 3 users 88% 13 December 2011 в 20:21
поделиться

Вот мое суждение в C++

, я пытался ввести такое небольшое ограничение на тип итератора, как я мог так это решение принимать просто вперед итератор, и это может быть const_iterator. Это должно работать с любым стандартным контейнером. В случаях, где аргументы не имеют смысла, он бросает станд.:: invalid_argumnent

#include <vector>
#include <stdexcept>

template <typename Fci> // Fci - forward const iterator
std::vector<std::vector<Fci> >
enumerate_combinations(Fci begin, Fci end, unsigned int combination_size)
{
    if(begin == end && combination_size > 0u)
        throw std::invalid_argument("empty set and positive combination size!");
    std::vector<std::vector<Fci> > result; // empty set of combinations
    if(combination_size == 0u) return result; // there is exactly one combination of
                                              // size 0 - emty set
    std::vector<Fci> current_combination;
    current_combination.reserve(combination_size + 1u); // I reserve one aditional slot
                                                        // in my vector to store
                                                        // the end sentinel there.
                                                        // The code is cleaner thanks to that
    for(unsigned int i = 0u; i < combination_size && begin != end; ++i, ++begin)
    {
        current_combination.push_back(begin); // Construction of the first combination
    }
    // Since I assume the itarators support only incrementing, I have to iterate over
    // the set to get its size, which is expensive. Here I had to itrate anyway to  
    // produce the first cobination, so I use the loop to also check the size.
    if(current_combination.size() < combination_size)
        throw std::invalid_argument("combination size > set size!");
    result.push_back(current_combination); // Store the first combination in the results set
    current_combination.push_back(end); // Here I add mentioned earlier sentinel to
                                        // simplyfy rest of the code. If I did it 
                                        // earlier, previous statement would get ugly.
    while(true)
    {
        unsigned int i = combination_size;
        Fci tmp;                            // Thanks to the sentinel I can find first
        do                                  // iterator to change, simply by scaning
        {                                   // from right to left and looking for the
            tmp = current_combination[--i]; // first "bubble". The fact, that it's 
            ++tmp;                          // a forward iterator makes it ugly but I
        }                                   // can't help it.
        while(i > 0u && tmp == current_combination[i + 1u]);

        // Here is probably my most obfuscated expression.
        // Loop above looks for a "bubble". If there is no "bubble", that means, that
        // current_combination is the last combination, Expression in the if statement
        // below evaluates to true and the function exits returning result.
        // If the "bubble" is found however, the ststement below has a sideeffect of 
        // incrementing the first iterator to the left of the "bubble".
        if(++current_combination[i] == current_combination[i + 1u])
            return result;
        // Rest of the code sets posiotons of the rest of the iterstors
        // (if there are any), that are to the right of the incremented one,
        // to form next combination

        while(++i < combination_size)
        {
            current_combination[i] = current_combination[i - 1u];
            ++current_combination[i];
        }
        // Below is the ugly side of using the sentinel. Well it had to haave some 
        // disadvantage. Try without it.
        result.push_back(std::vector<Fci>(current_combination.begin(),
                                          current_combination.end() - 1));
    }
}
3
ответ дан 3 revs, 2 users 99% 13 December 2011 в 20:21
поделиться

У меня был алгоритм перестановки, который я использовал для Эйлера проекта в Python:

def missing(miss,src):
    "Returns the list of items in src not present in miss"
    return [i for i in src if i not in miss]


def permutation_gen(n,l):
    "Generates all the permutations of n items of the l list"
    for i in l:
        if n<=1: yield [i]
        r = [i]
        for j in permutation_gen(n-1,missing([i],l)):  yield r+j

, Если

n<len(l) 

у Вас должна быть вся комбинация, Вам нужно без повторения, Вам нужен он?

Это - генератор, таким образом, Вы используете его в чем-то вроде этого:

for comb in permutation_gen(3,list("ABCDEFGH")):
    print comb 
6
ответ дан 4 revs, 2 users 98% 13 December 2011 в 20:21
поделиться

В Python как Andrea Ambu, но не hardcoded для выбора три.

def combinations(list, k):
    """Choose combinations of list, choosing k elements(no repeats)"""
    if len(list) < k:
        return []
    else:
        seq = [i for i in range(k)]
        while seq:
            print [list[index] for index in seq]
            seq = get_next_combination(len(list), k, seq)

def get_next_combination(num_elements, k, seq):
        index_to_move = find_index_to_move(num_elements, seq)
        if index_to_move == None:
            return None
        else:
            seq[index_to_move] += 1

            #for every element past this sequence, move it down
            for i, elem in enumerate(seq[(index_to_move+1):]):
                seq[i + 1 + index_to_move] = seq[index_to_move] + i + 1

            return seq

def find_index_to_move(num_elements, seq):
        """Tells which index should be moved"""
        for rev_index, elem in enumerate(reversed(seq)):
            if elem < (num_elements - rev_index - 1):
                return len(seq) - rev_index - 1
        return None   
0
ответ дан 3 revs, 2 users 98% 13 December 2011 в 20:21
поделиться

Я создал решение в SQL Server 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)
3
ответ дан 22 November 2019 в 22:18
поделиться

В C ++ следующая процедура будет производить все комбинации длины расстояние (first, k) между диапазоном [first, last):

#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
24
ответ дан 22 November 2019 в 22:18
поделиться

В C #:

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
189
ответ дан 22 November 2019 в 22:18
поделиться

Вот код, который я недавно написал на Java, который вычисляет и возвращает все комбинации элементов "num" из элементов "outOf" .

// author: Sourabh Bhat (heySourabh@gmail.com)

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);
    }
}
3
ответ дан 22 November 2019 в 22:18
поделиться

Вот элегантная, универсальная реализация на Scala, описанная в 99 Scala Problems.

object P26 {
  def flatMapSublists[A,B](ls: List[A])(f: (List[A]) => List[B]): List[B] = 
    ls match {
      case Nil => Nil
      case sublist@(_ :: 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 :: _}
    }
}
9
ответ дан 22 November 2019 в 22:18
поделиться

Могу я представить свое рекурсивное решение этой проблемы с помощью Python?

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

Мне нравится простота.

75
ответ дан 22 November 2019 в 22:18
поделиться

Вот простой код, который печатает все комбинации C (n, m). Он работает путем инициализации и перемещения набора индексов массива, указывающих на следующую допустимую комбинацию. Индексы инициализируются так, чтобы указывать на наименьшие m индексов (лексикографически наименьшую комбинацию). Затем, начиная с m-го индекса, мы пытаемся продвинуть индексы вперед. если индекс достиг своего предела, мы пробуем предыдущий индекс (вплоть до индекса 1). Если мы можем переместить индекс вперед, мы сбрасываем все большие индексы.

m=(rand()%n)+1; // m will vary from 1 to n

for (i=0;i<n;i++) a[i]=i+1;

// we want to print all possible C(n,m) combinations of selecting m objects out of n
printf("Printing C(%d,%d) possible combinations ...\n", n,m);

// This is an adhoc algo that keeps m pointers to the next valid combination
for (i=0;i<m;i++) p[i]=i; // the p[.] contain indices to the a vector whose elements constitute next combination

done=false;
while (!done)
{
    // print combination
    for (i=0;i<m;i++) printf("%2d ", a[p[i]]);
    printf("\n");

    // update combination
    // method: start with p[m-1]. try to increment it. if it is already at the end, then try moving p[m-2] ahead.
    // if this is possible, then reset p[m-1] to 1 more than (the new) p[m-2].
    // if p[m-2] can not also be moved, then try p[m-3]. move that ahead. then reset p[m-2] and p[m-1].
    // repeat all the way down to p[0]. if p[0] can not also be moved, then we have generated all combinations.
    j=m-1;
    i=1;
    move_found=false;
    while ((j>=0) && !move_found)
    {
        if (p[j]<(n-i)) 
        {
            move_found=true;
            p[j]++; // point p[j] to next index
            for (k=j+1;k<m;k++)
            {
                p[k]=p[j]+(k-j);
            }
        }
        else
        {
            j--;
            i++;
        }
    }
    if (!move_found) done=true;
}
1
ответ дан 22 November 2019 в 22:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: