Гость
Форумы / Программирование [игнор отключен] [закрыт для гостей] / Генерация сочетаний из N по K / 25 сообщений из 174, страница 1 из 7
23.01.2021, 12:07
    #40038239
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
Привет кодеры и прочие сочувствующие!

Сегодня суббота и субботняя задачка. В продолжение топика Милторга 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
23.01.2021, 12:44
    #40038247
Aleksandr Sharahov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
http://www.guildalfa.ru/alsha/node/26

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

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


не обратил внимания, что все уже закодил ))
...
Рейтинг: 0 / 0
23.01.2021, 13:43
    #40038258
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
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
23.01.2021, 13:57
    #40038261
Aleksandr Sharahov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
mayton,

да, верно.

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

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

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


да, верно.

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

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



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

это внешние процедуры для отладки,
все соберется без них,
ну или можно заменить на WriteLn.
...
Рейтинг: 0 / 0
23.01.2021, 15:29
    #40038294
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
ОК. Попробую собрать.
...
Рейтинг: 0 / 0
23.01.2021, 16:43
    #40038303
Aleksandr Sharahov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
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
23.01.2021, 20:47
    #40038332
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
mayton
Сегодня суббота и субботняя задачка. В продолжение топика Милторга 22260285

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

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

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

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

Ему там многие пытались втолковать, что его задача решается по-другому,
через генерацию сочетаний, но он не верит.
...
Рейтинг: 0 / 0
23.01.2021, 23:23
    #40038348
Anatoly Moskovsky
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
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
23.01.2021, 23:34
    #40038350
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
Anatoly Moskovsky, спасибо большое. Будем смотреть.
...
Рейтинг: 0 / 0
23.01.2021, 23:35
    #40038351
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
Aleksandr Sharahov, я еще в процессе. Возможно кое-что спрошу по оформлению main процедуры в Паскалях.
...
Рейтинг: 0 / 0
23.01.2021, 23:39
    #40038352
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
Dima T
mayton
Сегодня суббота и субботняя задачка. В продолжение топика Милторга 22260285

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


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

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

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

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

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

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

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

Можно брать первые (или последние) k, то что нам надо мы получим, но с повторами. Надо посмотреть что получается, может нужная нам последовательность генерится в начале той последовательности. Надо потестить, я пока не за компом, вечером попробую.
...
Рейтинг: 0 / 0
24.01.2021, 16:09
    #40038426
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
Напутал, std::perm это размещения, а надо сочетания. Много лишних будет.
...
Рейтинг: 0 / 0
24.01.2021, 19:07
    #40038444
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
Написал получение следующей комбинации в массив
Код: 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
24.01.2021, 19:48
    #40038445
Anatoly Moskovsky
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
Dima T,

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

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

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

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

Решение почти в лоб. У нас есть отсортированный вектор n, из него нужно выбрать m чисел, есть k = n - m подставляемых чисел. Нужен итератор, который будет перебирать занимаемые позиции для подставляемых, затем один раз проходить уже отсортированный веткор m, исключая позиции в итераторе, после чего идти по подставляемым числам k в итераторе по возрастанию и добавлять подставленное.
Завтра попробую найти минутку переписать итератор из треда Милторга, чтобы было понятнее.
...
Рейтинг: 0 / 0
24.01.2021, 20:10
    #40038449
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
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
24.01.2021, 20:17
    #40038450
Dima T
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генерация сочетаний из N по K
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
Форумы / Программирование [игнор отключен] [закрыт для гостей] / Генерация сочетаний из N по K / 25 сообщений из 174, страница 1 из 7
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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