powered by simpleCommunicator - 2.0.48     © 2025 Programmizd 02
Форумы / Программирование [игнор отключен] [закрыт для гостей] / Генерация сочетаний из N по K
25 сообщений из 174, страница 1 из 7
Генерация сочетаний из N по K
    #40038239
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Привет кодеры и прочие сочувствующие!

Сегодня суббота и субботняя задачка. В продолжение топика Милторга 22260285

Вкратце.

Input:
Код: sql
1.
2.
3
1, 2, 3, 4



Output:
Код: sql
1.
2.
3.
4.
1 2 3
1 2 4
1 3 4
2 3 4



Сочетания. Что еще тут добавить. Но нас будет интересовать стаблильный порядок. Тоесть
если в исходном векторе были 1,2,3,4 - то на выходе последовательности тоже должны
быть монотонные без убывания.

Проверка количества штук по формуле: C(k,n) = n! / (n - k)! * k!

Или по треугольничку Паскаля.

Что нам интересно рассматривать:
1. Большие величиные N. Например от 100.
2. Алгоритмические оптимизации.
3. Перформанс.

Не приветствуется
1. Нудотство и все вопросы типа зачем это.
2. Готовые приложения без исходников.
3. Обсуждение задачи Милторга.
4. Юзание баз данных.
5. Обсуждение частных и вырожденных случаев когда N=K или что-то равно 1.

Языки разработки
Любые С++/C#/Delphi/JS

Библиотеки
Если хотите - юзайте - но приложите их исходник чтобы мы могли понять алгоритм и идею.

По сабжу у меня пока нет идей. Так что стартую вместе с вами.

Go-Go кодить.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038247
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
http://www.guildalfa.ru/alsha/node/26

не обратил внимания, что нужны не все,
буду кодить )
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038249
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov
http://www.guildalfa.ru/alsha/node/26

не обратил внимания, что нужны не все,
буду кодить )


не обратил внимания, что все уже закодил ))
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038258
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov
Aleksandr Sharahov
http://www.guildalfa.ru/alsha/node/26

не обратил внимания, что нужны не все,
буду кодить )


не обратил внимания, что все уже закодил ))


Вот это наш вариант?
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
//перебор сочетаний из n по k элементов в алфавитном порядке
procedure GenerateCombinations(n, k: integer);
var
  s: ShortString;
  i, start: integer;
  chlast, ch: AnsiChar;
begin;
  if (k<=0) or (k>maxlength) or (n<k) then exit;
 
  SetLength(s,k);
  for i:=1 to k do s[i]:=AnsiChar((Ord(chfirst)-1)+i);
  ProcessString(s);
 
  //если существует более одного сочетания, то крутим цикл
  if k<n then begin;
    chlast:=AnsiChar(Ord(s[1])-1+n);
    start:=k;
    while true do begin;
      if s[k]<>chlast then start:=k
      else begin;
        dec(start); if start<=0 then break;
        end;
      i:=start;
      ch:=s[start];
      repeat;
        inc(ch); s[i]:=ch; inc(i);
        until i>k;
      ProcessString(s);
      end;
    end;
  end;
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038261
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton,

да, верно.

а вызовы ProcessString(s) - это печать или обработка строки
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038276
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Об ограничениях. Правильно ли я понимаю что мы здесь оперируем не целыми числами
а символами (Char, AnsiChar)?

И следует ли из этого ограничение на 256 вариаций n элементов?
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038291
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Об ограничениях. Правильно ли я понимаю что мы здесь оперируем не целыми числами
а символами (Char, AnsiChar)?

И следует ли из этого ограничение на 256 вариаций n элементов?


да, верно.

Тип ShortString это массив символов (байтов), нулевой элемент которого - текущая длина строки.
При необходимости это можно переписать с использованием целочисленного массива.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038292
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Хороший исходник. Но в нем слишком много паскаля. Я вот думаю как его обобщить. И по возможности
выкосить всякие линки на

Код: pascal
1.
Form1.Memo1.Lines.Add(Copy(s,1,cut)+'    '+IntToStr(ItemCount));



Насколько я понимаю... это сниппеты Forms-приложения, а мне чтоб собрать его под FreePascal надо
будет сделать несколько хирургических замен.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038293
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton,

это внешние процедуры для отладки,
все соберется без них,
ну или можно заменить на WriteLn.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038294
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ОК. Попробую собрать.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038303
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton,

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

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
type
  TCombination = array of integer;

//только для отладки
procedure ShowCombination(const c: TCombination);
var
  s: AnsiString;
  i: integer;
begin;
  for i:=0 to Length(c)-1 do s:=s+AnsiChar(Ord('A')+c[i]);
  //Form1.Memo1.Lines.Add(s);
  WriteLn(s);
  end;

procedure ProcessCombination(const c: TCombination);
begin;
  ShowCombination(c); //отладка - закомментировать при работе

  //тут делаем что-то с массивом
  end;

//перебор сочетаний из n элементов [0..n-1] по k в алфавитном порядке
procedure GenerateCombinations(n, k: integer);
var
  c: TCombination;
  i, istart, v, vlast: integer;
begin;
  if (k<=0) or (k>n) then exit;

  SetLength(c, k);
  for i:=0 to k-1 do c[i]:=i;
  ProcessCombination(c);

  //если существует более одного сочетания, то крутим цикл
  if k<n then begin;
    vlast:=c[0]-1+n;
    istart:=k-1;
    while true do begin;
      if c[k-1]<>vlast then istart:=k-1
      else begin;
        dec(istart); if istart<0 then break;
        end;
      i:=istart;
      v:=c[istart];
      repeat;
        inc(v); c[i]:=v; inc(i);
        until i>=k;
      ProcessCombination(c);
      end;
    end;
  end;

GenerateCombinations(5, 3);
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038332
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Сегодня суббота и субботняя задачка. В продолжение топика Милторга 22260285

Топик тот начинается про сортировку, извиняйте не прочитал целиком и не готов 14 страниц прочитать, мне ковид на НГ подарили, не до того было.
Если можно дай ссылку или вкратце перескажи как сортировка переродилась в перестановки?

В С++ перестановки в массиве вроде есть штатно.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038334
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T,

насколько я понял, автор той ветки довольно причудливо решает свою задачу.

Он генерирует перестановки двух множеств и затем сравнивает их,
чтобы найти совпадающие элементы,
при этом сортировка нужна ему для удобства сравнения нагенерированного,
в общем, ужас-ужас.

Ему там многие пытались втолковать, что его задача решается по-другому,
через генерацию сочетаний, но он не верит.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038348
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Что нам интересно рассматривать:
1. Большие величиные N. Например от 100.


А в чем сложность больших N? Разве что переполнение стека при рекурсивной реализации.
Но там вообще глубина стека равна K (размер сочетаний), а не N.
А К у нас вряд ли будет достаточно большим чтобы переполнить стек.

Ну и чтобы два раза не вставать:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
#include <iostream>
#include <vector>

class RecursiveCombinator
{
public:
    RecursiveCombinator(size_t k, std::vector<int> items, bool print)
        : k(k)
        , n(items.size())
        , items(std::move(items))
        , print(print)
    {
        curr.resize(k);
    }

    void generate() noexcept
    {
        std::cout << "k=" << k << ", n=" << n << ", print=" << print << "\n";
        generate(0, 0);
        std::cout << "total: " << count << "\n";
    }

private:
    // алгоритм
    void generate(size_t pos, size_t val) noexcept
    {
        const size_t limit = n - k + pos + 1;
        for (; val < limit; val++) {
            curr[pos] = val;
            if (pos == k - 1) {
                print_current();    
            }
            else {
                generate(pos + 1, val + 1);
            }
        }
    }

    void print_current() noexcept
    {
        count++;
        if (print) {
            for (size_t i = 0; i < k; i++) {
                std::cout << items[curr[i]] << " ";
            }
            std::cout << "\n";
        }
    }

private:
    const size_t k;
    const size_t n;
    const std::vector<int> items;
    const bool print;
    std::vector<size_t> curr;
    size_t count = 0;
};


std::vector<int> iota(size_t n)
{
    std::vector<int> rv(n);
    for (size_t i = 0; i < n; i++) {
        rv[i] = i + 1;
    }
    return rv;
}

int main(int argc, char* argv[])
{
    std::cout.sync_with_stdio(false);
    size_t k = 3;
    int n = 4;
    bool print = true;
    if (argc > 2) {
        k = atol(argv[1]);
        n = atol(argv[2]);
    }
    if (argc > 3) {
        print = argv[3][0] == 'p';
    }
    RecursiveCombinator comb{k, iota(n), print};
    comb.generate();
    return 0;
}



gcc v8.3.0 -O3 -DNDEBUG / Intel i7-8565U CPU @ 1.80GHz$ ./comb 3 4
k=3, n=4, print=1
1 2 3
1 2 4
1 3 4
2 3 4
total: 4

$ ./comb 3 5
k=3, n=5, print=1
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
total: 10

$ time ./comb 3 100 -
k=3, n=100, print=0
total: 161700

real 0m0.005s
user 0m0.000s
sys 0m0.005s


$ time ./comb 4 100 -
k=4, n=100, print=0
total: 3921225

real 0m0.027s
user 0m0.027s
sys 0m0.001s


$ time ./comb 6 100 -
k=6, n=100, print=0
total: 1192052400

real 0m1.721s
user 0m1.721s
sys 0m0.001s


$ time ./comb 1000 1000 -
k=1000, n=1000, print=0
total: 1

real 0m0.004s
user 0m0.001s
sys 0m0.004s
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038350
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky, спасибо большое. Будем смотреть.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038351
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov, я еще в процессе. Возможно кое-что спрошу по оформлению main процедуры в Паскалях.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038352
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
mayton
Сегодня суббота и субботняя задачка. В продолжение топика Милторга 22260285

Топик тот начинается про сортировку, извиняйте не прочитал целиком и не готов 14 страниц прочитать, мне ковид на НГ подарили, не до того было.
Если можно дай ссылку или вкратце перескажи как сортировка переродилась в перестановки?


Это ужасно чувак. Выздоравливай! Я тоже болел. Очень неприятные последствия. Особенно с потерей вкуса.
И какая-то хроническая усталость после этого надолго сохранилась.

По поводу топиков Милторга. Не надо его смотреть. У него там - другая задача. У него даны наборы
векторов и он их сортирует чтоб группировать. С моей точки зрения его задача решается на SQL
но я так и не понял решил он ее для себе или нет и что вообще он хочет дальше? Излагает сумбурно.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038356
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
В С++ перестановки в массиве вроде есть штатно.

В С++ есть стандартный генератор перестановок класса "размещений". Через std::next_perm.
И для случая когда n==k. Количество считается просто как факториал от n.

А нас интересуют сочетания (когда порядок не учитываем) и для общего случая когда k<=n
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038375
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Dima T
В С++ перестановки в массиве вроде есть штатно.

В С++ есть стандартный генератор перестановок класса "размещений". Через std::next_perm.
И для случая когда n==k. Количество считается просто как факториал от n.

А нас интересуют сочетания (когда порядок не учитываем) и для общего случая когда k<=n

А нельзя с помощью std::next_permutation() как-то получить нужную нам последовательность?

Можно брать первые (или последние) k, то что нам надо мы получим, но с повторами. Надо посмотреть что получается, может нужная нам последовательность генерится в начале той последовательности. Надо потестить, я пока не за компом, вечером попробую.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038426
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Напутал, std::perm это размещения, а надо сочетания. Много лишних будет.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038444
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Написал получение следующей комбинации в массив
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
#include <stdint.h>
#include <stdio.h>
#include <time.h>

// Следующее сочетание k элементов диапазона 1...n в массив a[]
// Возвращает true если оно есть
bool nexn_combinations(int* a, int k, int n) {
	if(a[0] == 0) { // Инициализация
		for (int i = 0; i < k; i++) a[i] = i + 1;
		return true;
	}
	for(int i = k-1, max = n; i >= 0; i--, max--) {
		if(a[i] < 1 || a[i] > max) {  // за границей диапазона
			printf("ERROR\n"); 
			return false; 
		}
		if (a[i] < max) {
			a[i]++;
			return true;
		} 
		if(i > 0) {
			a[i] = a[i - 1] + 2;
			if (a[i - 1] == max - 1) {
				a[i]--;
			}
		}
		for (int j = i + 1; j < k; j++) a[j] = a[j - 1] + 1;
	}
	return false;
}

//**********************************************
#define K 6
#define N 100

int main(int argc, char**argv)
{
	int a[K] = {0};
	int cnt = 0;
	printf("C(%d, %d)\n", K, N);
	while(nexn_combinations(a, K, N)) {
		cnt++;
#if(N < 7)
		for(int& i : a) printf("%d ", i);
		printf("\n");
#endif
	}
	printf("Total: %d Time %d ms", cnt, clock());
	return 0;
}


Результаты схожие с Анатолием
Код: plaintext
1.
C(6, 100)
Total: 1192052400 Time 1803 ms
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038445
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T,

Надо выкинуть из ф-и условия "Инициализация" и "за границей диапазона".
Для алгоритма они не нужны, но путаются под ногами у предсказателя переходов.

Я когда у себя закоментарил условие печати, то на 7% быстрее стало.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038446
Фотография crutchmaster
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton,

Надо какие-нибудь контрольные примеры, чтобы было на чём мериться.

mayton
По сабжу у меня пока нет идей.

Решение почти в лоб. У нас есть отсортированный вектор n, из него нужно выбрать m чисел, есть k = n - m подставляемых чисел. Нужен итератор, который будет перебирать занимаемые позиции для подставляемых, затем один раз проходить уже отсортированный веткор m, исключая позиции в итераторе, после чего идти по подставляемым числам k в итераторе по возрастанию и добавлять подставленное.
Завтра попробую найти минутку переписать итератор из треда Милторга, чтобы было понятнее.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038449
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
Dima T,

Надо выкинуть из ф-и условия "Инициализация" и "за границей диапазона".
Для алгоритма они не нужны, но путаются под ногами у предсказателя переходов.

Я когда у себя закоментарил условие печати, то на 7% быстрее стало.



Инициализацию перенес в main(), согласен что в функции ей там не место, а "за границей диапазона" это контроль ошибок, его надо оставить.
Самое забавное что время никак не поменялось. MSVC 2019, может в G++ по другому будет.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
#include <stdint.h>
#include <stdio.h>
#include <time.h>

// Следующее сочетание k элементов диапазона 1...n в массив a[]
// Возвращает true если оно есть
bool nexn_combinations(int* a, int k, int n) {
	for (int i = k - 1, max = n; i >= 0; i--, max--) {
		if (a[i] < 1 || a[i] > max) {  // за границей диапазона
			printf("ERROR\n");
			return false;
		}
		if (a[i] < max) {
			a[i]++;
			return true;
		}
		if (i > 0) {
			a[i] = a[i - 1] + 2;
			if (a[i - 1] == max - 1) {
				a[i]--;
			}
		}
		for (int j = i + 1; j < k; j++) a[j] = a[j - 1] + 1;
	}
	return false;
}



//**********************************************
#define K 6
#define N 100

int main(int argc, char**argv)
{
	printf("C(%d, %d)\n", K, N);
	int a[K];
	for (int i = 0; i < K; i++) a[i] = i + 1;
	int cnt = 0;
	do {
		cnt++;
#if(N < 7)
		for(int& i : a) printf("%d ", i);
		printf("\n");
#endif
	} while (nexn_combinations(a, K, N));
	printf("Total: %d Time %d ms", cnt, clock());
	return 0;
}


Если "за границей диапазона" закомментить, то время 1.67 сек или на 7% быстрее
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038450
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
crutchmaster
Надо какие-нибудь контрольные примеры, чтобы было на чём мериться.

Все есть в первом посте. Для C(3,4) mayton все комбинации привел.
Там же общая формула сколько должно быть комбинаций: C(k,n) = n! / (n - k)! * k!

Я на С(4,6) отлаживал:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
1 2 3 4
1 2 3 5
1 2 3 6
1 2 4 5
1 2 4 6
1 2 5 6
1 3 4 5
1 3 4 6
1 3 5 6
1 4 5 6
2 3 4 5
2 3 4 6
2 3 5 6
2 4 5 6
3 4 5 6

Проверка С(6, 100) - 1'192'052'400 комбинаций
...
Рейтинг: 0 / 0
25 сообщений из 174, страница 1 из 7
Форумы / Программирование [игнор отключен] [закрыт для гостей] / Генерация сочетаний из N по K
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]