powered by simpleCommunicator - 2.0.49     © 2025 Programmizd 02
Форумы / Программирование [игнор отключен] [закрыт для гостей] / Генерация сочетаний из N по K
174 сообщений из 174, показаны все 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
Генерация сочетаний из N по K
    #40038465
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
а "за границей диапазона" это контроль ошибок, его надо оставить.

Это должен быть assert() включаемый только в дебаг сборках.
Потому что в это отладочный код.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038473
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
Dima T
а "за границей диапазона" это контроль ошибок, его надо оставить.

Это должен быть assert() включаемый только в дебаг сборках.
Потому что в это отладочный код.

Может и так, но эти 7% мало что меняют. Тут важно оценить сложность алгоритма, ясно что не O(1). Например если потребуется 5 лет, то будет пофиг 5 лет или 5 лет и 4 месяца.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038501
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T, под gcc выдает варнинг

Код: sql
1.
2.
3.
4.
5.
6.
7.
perm-k-n.cpp: In function ‘int main(int, char**)’:
perm-k-n.cpp:45:29: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘clock_t’ {aka ‘long int’} [-Wformat=]
   45 |     printf("Total: %d Time %d ms", cnt, clock());
      |                            ~^           ~~~~~~~
      |                             |                |
      |                             int              clock_t {aka long int}
      |                            %ld
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038505
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
mayton
Что нам интересно рассматривать:
1. Большие величиные N. Например от 100.


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

В данном конкретном алгоритме переполнение стека нам не грозит. Слишком малые у нас запросы.
А на больших - мы дождемся финала когда остынет солнце.

Что интересовало? Некие общие подходы к комбинаторным алгоритмам. Генераторы последовательностей.
Ленивые списки. Хвостовая рекурсия в продолжение моего одноименного топика. (Всегда-ли возможно?)
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038519
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пока быстрее работает рекурсивный алгоритм Анатолия. На генерации 6 из 45 занимает меньше 1 секунды
против 9 секунд итеративного метода Димы.

В коде Анатолия я только счетчик перенес из принта в основную функцию потому-что при отключени вывода
на печать - выключается и подсчет найденных сочетаний.

Код: 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.
    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();    
                count++;
            }
            else {
                generate(pos + 1, val + 1);
            }
        }
    }

    void print_current() noexcept
    {
        // deleted
        if (print) {
            for (size_t i = 0; i < k; i++) {
                std::cout << items[curr[i]] << " ";
            }
            std::cout << "\n";
        }
    }
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038522
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Александр. Я про твой код не забыл. Просто пока не хватает терпенья оформить все формальности для того чтоб собрать
туловище алгоритма.

make.sh
Код: sql
1.
2.
3.
4.
5.
#!/bin/bash

rm -f perm-k-n.exe

fpc -CX -O3 -XX -vewnhi -Fi. -Fu. -FU. perm-k-n.lpr && mv perm-k-n perm-k-n.exe



Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
$ ./make.sh
Hint: Start of reading config file /etc/fpc.cfg
Hint: End of reading config file /etc/fpc.cfg
Free Pascal Compiler version 3.0.4+dfsg-23 [2019/11/25] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling perm-k-n.lpr
perm-k-n.lpr(10,34) Warning: Local variable "s" of a managed type does not seem to be initialized
perm-k-n.lpr(53,1) Fatal: Syntax error, "BEGIN" expected but "identifier GENERATECOMBINATIONS" found
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode



perm-k-n.lpr - это последний сорс который ты публиковал.

Уж извини если я иногда - нетерпеливый.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038551
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Тюнинганул алгоритм, заменил проверку на assert(), но стало медленнее (( 1.96 сек вместо 1.67 тут 22268468
исходник
Код: 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.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

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

//**********************************************
int main(int argc, char**argv)
{
	int k, n;
	if (argc > 2) {
		k = atoi(argv[1]);
		n = atoi(argv[2]);
	} else {
		printf("Too few arguments\n");
		return 0;
	}

	printf("C(%d, %d)\n", k, n);
	int *a = new int[k];
	for (int i = 0; i < k; i++) a[i] = i + 1;
	int cnt = 0;
	do {
		cnt++;
#ifdef _DEBUG
		for (int i = 0; i < k; i++) printf("%d ", a[i]);
		printf("\n");
#else
		if((cnt & 0x3FFFFFF) == 0) printf("Total: %d\r", cnt);
#endif
	} while (nexn_combinations(a, k, n));

	printf("Total: %d\n", cnt);
#if defined(_WIN32) || defined(_WIN64)
	printf("Time %d ms", clock());
#endif
	delete[] a;
	return 0;
}


mayton
Пока быстрее работает рекурсивный алгоритм Анатолия. На генерации 6 из 45 занимает меньше 1 секунды
против 9 секунд итеративного метода Димы.

Затестил алгоритм Анатолия без вывода - 1.63 сек. Странно конечно что рекурсия быстрее, но не в разы. Проверь, может с разными ключами компилировал.

PS Оказалось эта проверка отъедает 0.12 сек, можно закаментить, без нее 1.84 сек.
Код: plaintext
1.
if((cnt & 0x3FFFFFF) == 0) printf("Total: %d\r", cnt);



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

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

И сам current вектор - глобальный и модифицируется на каждом рекуррентном вызове.

Я думаю что мы достигли хорошей величины перформанса и дальше - узким местом
будет просто передача найденных сочетаний в consumer или в то приложение
которое будет его юзать.

Здесь я хотел-бы это сделать на streams/actors/ или каких-то асинхронных технологиях
через кольцевой буфер.

Тоесть чтобы генерация сочетаний шла отдельным потоком а уже их извлечение отдельно.

Работа с STDOUT - это конечно ужас-ужас но пускай пока она будет как основной вариант
межпроцессного протокола.

И в данной задаче (которая удачно ложится на списки ФП) я еще для себя хотел реализовать
ее на Haskell/Lisp если будет время.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038635
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
В идеале у нас получается что вся полезная информация по рекурсии лежит в стеке.
Это два целых числа.

На самом деле там одно число только нужно. Второе всегда равно уровню вложенности, который легко вычисляется. Я допускаю что и второе можно вычислить, и тогда можно вообще без стека обойтись. Но мне некогда думать ))

Вот еще вчера наклепал нерекурсивный алгоритм с явным стеком.
Скорость чуть хуже рекурсивного со стеком на основе std::deque и чуть лучше с boost::static_vector (но жесткий лимит на глубину стека).

Код: sql
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.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
#include <iostream>
#include <stack>
#include <vector>

//#include <boost/container/small_vector.hpp>
//#include <boost/container/static_vector.hpp>

#define LIKELY(x)       __builtin_expect(!!(x), 1)
#define UNLIKELY(x)     __builtin_expect(!!(x), 0)


class NonRecursiveCombinator
{
public:
    NonRecursiveCombinator(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_impl();
        std::cout << "total: " << count << "\n";
    }

private:
    // алгоритм
    void generate_impl() noexcept
    {
        struct State {
            size_t val;
        };
        //std::stack<State, boost::container::small_vector<State, 1000>> stack;
        //std::stack<State, std::vector<State>> stack;
        std::stack<State> stack; // deque; 2nd fastest
        //std::stack<State, boost::container::static_vector<State, 1000>> stack; // fastest
        size_t pos = 0;
        size_t limit = n - k + pos + 1;
        size_t val = 0;
        while (true) {
            for (; val < limit; val++) {
                curr[pos] = val;
                if (pos == k - 1) {
                    print_current();
                }
                else {
                    stack.push({val + 1});
                    pos++;
                    limit++;
                }
            }
            if (stack.empty())
                break;
            val = stack.top().val;
            stack.pop();
            pos--;
            limit--;
        }
    }

    void print_current() noexcept
    {
        count++;
        if (UNLIKELY(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';
    }
    NonRecursiveCombinator comb{k, iota(n), print};
    comb.generate();
    return 0;
}


...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038639
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky, нет-нет. Не надо разматывать рекурсию в автомат со стеком.
Для асинхронного актора о котором я думаю это вообще не нужно. А для внешнего
вида исходника - это только ухудшает чтение.

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

Нерекурсивный легче оптимизировать.
Сами же писали что скорость одна из задач.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038648
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вот кстати, было пару минут, вообще убрал стек из нерекурсивного алгоритма.
На скорость не повлияло :)

Код: sql
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.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
#include <iostream>
#include <vector>

#define LIKELY(x)       __builtin_expect(!!(x), 1)
#define UNLIKELY(x)     __builtin_expect(!!(x), 0)


class NonRecursiveCombinator
{
public:
    NonRecursiveCombinator(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_impl();
        std::cout << "total: " << count << "\n";
    }

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

    void print_current() noexcept
    {
        count++;
        if (UNLIKELY(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';
    }
    NonRecursiveCombinator comb{k, iota(n), print};
    comb.generate();
    return 0;
}


...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038656
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Давай затестим на больших числах.

Кроме того нерекурсивный - гибче. Если какое-то условие надо вставить то рекурсивный
остается красив и компактен а итерационный превращается в нагромождение необъяснимой логики.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038659
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Затестил алгоритм Анатолия без вывода - 1.63 сек. Странно конечно что рекурсия быстрее, но не в разы. Проверь, может с разными ключами компилировал.

PS Оказалось эта проверка отъедает 0.12 сек, можно закаментить, без нее 1.84 сек.

ОК спасибо.
Проверю вечером на своём AMD. Щас сижу на ограниченном десктопе где неудобно собирать и бенчмаркать.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038661
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Пока быстрее работает рекурсивный алгоритм Анатолия. На генерации 6 из 45 занимает меньше 1 секунды
против 9 секунд итеративного метода Димы.

Я потестил 6 из 45 считает 18 мс. Что-то у тебя не то с замером или компилятором.

Вот самый быстрый код. С(6,100) за 1.2 сек. Только для каждого k надо будет отдельно код написать

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
// Все сочетания из 1...n по 6
void all_combinations_6(int n) {
	printf("C(6, %d)\n", n);
	int cnt = 0;
	for (int i1 = 1; i1 <= n - 5; i1++)
		for (int i2 = i1 + 1; i2 <= n - 4; i2++)
			for (int i3 = i2 + 1; i3 <= n - 3; i3++)
				for (int i4 = i3 + 1; i4 <= n - 2; i4++)
					for (int i5 = i4 + 1; i5 <= n - 1; i5++)
						for (int i6 = i5 + 1; i6 <= n; i6++)
						{
							//printf("%d %d %d %d %d %d\n", i1, i2, i3, i4, i5, i6);
							cnt++;
							if ((cnt & 0x3FFFFFF) == 0) printf("Total: %d\r", cnt);
						}
	printf("Total: %d\n", cnt);
}


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

Код: plaintext
1.
if((cnt & 0x3FFFFFF) == 0) printf("Total: %d\r", cnt);


Не может этого быть. Маска + сравнение... Скорее всего тормозит printf даже при печати в /dev/null
ведь ему надо сделать десятичное форматирование cnt и еще ряд каких-то блокировок и фиксаций в разделяемый
объект стандартного вывода.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038667
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T

Только для каждого k надо будет отдельно код написать

Ахахах!
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038668
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
mayton
Пока быстрее работает рекурсивный алгоритм Анатолия. На генерации 6 из 45 занимает меньше 1 секунды
против 9 секунд итеративного метода Димы.

Я потестил 6 из 45 считает 18 мс. Что-то у тебя не то с замером или компилятором.

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

Код: plaintext
1.
if((cnt & 0x3FFFFFF) == 0) printf("Total: %d\r", cnt);


Не может этого быть. Маска + сравнение... Скорее всего тормозит printf даже при печати в /dev/null
ведь ему надо сделать десятичное форматирование cnt и еще ряд каких-то блокировок и фиксаций в разделяемый
объект стандартного вывода.

Для C(6, 100) эта строчка выполняется 1.2 миллиарда раз. Т.е. (cnt & 0x3FFFFFF) == 0 выполняется со скоростью примерно 10 миллиардов раз в секунду.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038671
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Dima T

Только для каждого k надо будет отдельно код написать

Ахахах!

Шаблонам же можно кучу однотипных функций компилировать, а мы чем хуже. Можно генератор кода написать

Данный код подходит как минимум для определения максимально возможной скорости, быстрее точно ни у кого не будет.

mayton
Я все собираю на GCC без параметров. Вечером - предоставлю больше сведений.

добавь ключи -O3 -DNDEBUG
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038674
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
mayton
пропущено...

Не может этого быть. Маска + сравнение... Скорее всего тормозит printf даже при печати в /dev/null
ведь ему надо сделать десятичное форматирование cnt и еще ряд каких-то блокировок и фиксаций в разделяемый
объект стандартного вывода.

Для C(6, 100) эта строчка выполняется 1.2 миллиарда раз. Т.е. (cnt & 0x3FFFFFF) == 0 выполняется со скоростью примерно 10 миллиардов раз в секунду.

Ну смотри... этот код на самом деле не должен срабатывать точно-точно каждые 2 мильярда итераций.
Это-ж для статистики? Тоесть я могу сюда поставить любую константу кратную числу вложенных
итераций от верхних циклов. Тогда эту проверку можно вообще убрать а printf просто вынести на 4-5
уровней циклов выше.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
void all_combinations_6(int n) {
	printf("C(6, %d)\n", n);
	int cnt = 0;
	for (int i1 = 1; i1 <= n - 5; i1++)
		for (int i2 = i1 + 1; i2 <= n - 4; i2++)
			for (int i3 = i2 + 1; i3 <= n - 3; i3++) {
                                printf("Total: %d\r", cnt);
				for (int i4 = i3 + 1; i4 <= n - 2; i4++)
					for (int i5 = i4 + 1; i5 <= n - 1; i5++)
						for (int i6 = i5 + 1; i6 <= n; i6++)
						{
							//printf("%d %d %d %d %d %d\n", i1, i2, i3, i4, i5, i6);
							cnt++;
							// if ((cnt & 0x3FFFFFF) == 0) printf("Total: %d\r", cnt);
						}
	printf("Total: %d\n", cnt);
        ....... тут добавить скобочек....
}
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038677
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
mayton
пропущено...

Ахахах!

Шаблонам же можно кучу однотипных функций компилировать, а мы чем хуже. Можно генератор кода написать

Кодогенерация для С++ в рантайме? .... Хм... эту тему надо вообще толкнуть отдельным топиком.

Насколько я помню тут ключевой вопрос - безопасность. В рантайме ты должен сгенерить неподписанную
никем *.dll -ку и динамически вызвать ее из главной процедуры. Антивирус сойдет с ума.

Это то что легко делается в языках типа JScript/Python/Node/Lisp через eval и то что почти
неизведанно как Антарктида в С++.

Откомментируйте кто в теме.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038681
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Dima T
пропущено...

Для C(6, 100) эта строчка выполняется 1.2 миллиарда раз. Т.е. (cnt & 0x3FFFFFF) == 0 выполняется со скоростью примерно 10 миллиардов раз в секунду.

Ну смотри... этот код на самом деле не должен срабатывать точно-точно каждые 2 мильярда итераций.
Это-ж для статистики? Тоесть я могу сюда поставить любую константу кратную числу вложенных
итераций от верхних циклов. Тогда эту проверку можно вообще убрать а printf просто вынести на 4-5
уровней циклов выше.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
void all_combinations_6(int n) {
	printf("C(6, %d)\n", n);
	int cnt = 0;
	for (int i1 = 1; i1 <= n - 5; i1++)
		for (int i2 = i1 + 1; i2 <= n - 4; i2++)
			for (int i3 = i2 + 1; i3 <= n - 3; i3++) {
                                printf("Total: %d\r", cnt);
				for (int i4 = i3 + 1; i4 <= n - 2; i4++)
					for (int i5 = i4 + 1; i5 <= n - 1; i5++)
						for (int i6 = i5 + 1; i6 <= n; i6++)
						{
							//printf("%d %d %d %d %d %d\n", i1, i2, i3, i4, i5, i6);
							cnt++;
							// if ((cnt & 0x3FFFFFF) == 0) printf("Total: %d\r", cnt);
						}
	printf("Total: %d\n", cnt);
        ....... тут добавить скобочек....
}


Не, так нельзя. Если ты закаментишь красное, то оптимизатор выкинет все циклы после твоего printf(). Я сначала без этой строчки написал, время работы 50 мс выдало.

Пусть будет, 1% времени на него расходуется.
Как выше писали: все-равно в реале вместо cnt++ внутри будет какое-то сохранение комбинации, а это на порядки дольше времени займет.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038688
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Насколько я помню тут ключевой вопрос - безопасность. В рантайме ты должен сгенерить неподписанную
никем *.dll -ку и динамически вызвать ее из главной процедуры. Антивирус сойдет с ума.

Думаю именно тут будет затык. Может еще админ права поотбирать.

Вообще я немного в другую сторону думал. Если известны часто используемые значения N, то для них прописать, а остальным универсальную запускать.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
void all_combinations(int k, int n) {
   switch(k) {
     case 4:
       all_combinations_4(n);
       break;

     case 5:
       all_combinations_5(n);
       break;

...

     default:
       all_combinations_universal(k, n);
   }
}


Взять ту же задачу милторга, я так понимаю у него вектора были фиксированного размера, т.е. известного на момент компиляции.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038704
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T, по этой формуле

C(k,n) = n! / (n - k)! * k!

Я не считал. Тут надо просто факториал заменять на гладкую функцию
но субъективно... если смотреть на треугольник Паскаля - то мемоизировать
нужно самый центр и низ треугольника где значения большие.

А на краях они достаточно малы и там нечего считать.

...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038709
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T

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

Я думаю что нам не стоит завязываться на те задачи. Там - фигня на постном масле а у нас обобщенный подход.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038721
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Dima T, по этой формуле

C(k,n) = n! / (n - k)! * k!

Я не считал. Тут надо просто факториал заменять на гладкую функцию
но субъективно... если смотреть на треугольник Паскаля - то мемоизировать
нужно самый центр и низ треугольника где значения большие.

А на краях они достаточно малы и там нечего считать.

(n - k)! * k! убывает по мере увеличения k, т.е. максимальное количество сочетаний будет в середине при k = n/2
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038752
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Это верно отчасти. Если зафиксировать N.

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

Т.е. первые разряды меняем универсальным способом, а для последних k вызываем функцию и внутри нее снимаем результаты.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038758
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Можешь на примере сочетаний показать? Непонятно....
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038760
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Например С(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

Допустим есть функция с3(m,n), которая выдает все сочетания по 3 в диапазоне m...n, далее первый разряд перебираем снаружи и вызываем c3() для генерации последних 3х разрядов
Код: plaintext
1.
2.
1 c3(2,6)
2 c3(3,6)
3 c3(4,6)
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038765
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ты хочешь мемоизировать последние 4 разряда? Ну ОК. Попробуй. Главное чтоб результат совпал с полным вариантом.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038778
booby
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton,

сорри что встреваю, но тебе может оказаться интересным:

В Oracle, начиная с 10й версии есть готовая sql-функция под это - POWERMULTISET_BY_CARDINALITY:

Код: plsql
1.
2.
select rownum as rn, cast(column_value as sys.ku$_ObjNumSet) as a_set
from table(POWERMULTISET_BY_CARDINALITY(sys.ku$_ObjNumSet(1,2,3,4,5,6),4)) t




упс... что-то поломалось, но на любом sqlfiddle это можно проверить...


что касается разбивки на диапазоны, то весь процесс можно представить как манипуляцию k-м разрядом + сдвиг.
т.е. - если к-1 разряд фиксирован, то для к-го - n-k+1 вариант заполнения.
затем "затираем" значение к-1 разряда (сдвигаем набор на единицу вправо), получаем еще n-k комбинаций.
...
сдвигая последовательно лесенкой разряды от к до n-k-1 получим все комбинации, но в другом порядке выдачи.
Это как идея без кода, мимоходом...
т.е. идея состоит в построении соответствия номера комбинации подходящему "сдвигу" и "номеру операции внутри него".
Тогда, может быть, все разбивается на набор независимых циклов в пределах "сдвигов"
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038780
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
booby, OK посмотрю. У меня растет объем технического долга по топику. Я еще Сашин исходник не смотрел
и не скомпилировал целиком.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038799
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Ты хочешь мемоизировать последние 4 разряда? Ну ОК. Попробуй. Главное чтоб результат совпал с полным вариантом.

"мемоизировать" тут не в тему, т.к. надо разные наборы, они не повторяются.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038819
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Оптимизировал немного рекурсивный и нерекурсивный алгоритмы.
Для 6 из 100, рекурсивный теперь дает 1с, нерекурсивный 1.2с

Исходники (цветом выделил изменения)
Рекурсивный
Код: 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.
86.
87.
88.
89.
#include <iostream>
#include <vector>

#define LIKELY(x)       __builtin_expect(!!(x), 1)
#define UNLIKELY(x)     __builtin_expect(!!(x), 0)

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";
        size_t count = generate(0, 0, 0);
        std::cout << "total: " << count << "\n";
    }

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

    void print_current() noexcept
    {
        if (UNLIKELY(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 size_t last_pos = k - 1;
    const std::vector<int> items;
    const bool print;
    std::vector<size_t> curr;
};


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;
}





Нерекурсивный
Код: 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.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
#include <iostream>
#include <vector>

#define LIKELY(x)       __builtin_expect(!!(x), 1)
#define UNLIKELY(x)     __builtin_expect(!!(x), 0)


class NonRecursiveCombinator
{
public:
    NonRecursiveCombinator(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";
        size_t count = generate_impl();
        std::cout << "total: " << count << "\n";
    }

private:
    // алгоритм
    size_t generate_impl() noexcept __attribute((noinline))
    {
        size_t pos = 0;
        size_t limit = n - k + pos + 1;
        const size_t last_pos = k - 1;
        size_t val = 0;
        size_t count = 0;
        while (true) {
            for (; val < limit; val++) {
                curr[pos] = val;
                if (pos == last_pos) {
                    count++; 
                    print_current();
                }
                else {
                    pos++;
                    limit++;
                }
            }
            if (pos == 0)
                break;
            pos--;
            limit--;
            val = curr[pos] + 1;
        }
        return count;
    }

    void print_current() noexcept 
    {
        if (UNLIKELY(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';
    }
    NonRecursiveCombinator comb{k, iota(n), print};
    comb.generate();
    return 0;
}


...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038831
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я обычно такой "for" разворачиваю в while.

Смысл в том что val++ теперь стоит ниже тела цикла и причинно-следственная связь логичнее смотрится.
Как в структурном программировании.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
        while (val < limit) {
            curr[pos] = val;
            if (pos == last_pos) {
                count++;
                print_current();    
            } else {
                count = generate(pos + 1, val + 1, count);
            }
            val++
        }
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038869
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Попробовал на вектор переделать и с итераторами переписать
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
bool next_combinations(std::vector<int>& a, int max) noexcept {
	for (auto it = a.end() - 1; it >= a.begin(); it--, max--) {
		assert(*it >= 1 && *it <= max);
		if (*it < max) {
			(*it)++;
			it++;
			for (; it != a.end(); it++) *it = *(it - 1) + 1;
			return true;
		}
	}
	return false;
}


Работает правильно, но вместо того чтобы выйти вылетаем с исключением Expression: can't decrement vector iterator before begin

Оно так и задумано что как только it ушел за начало, надо цикл завершать.

Не могу придумать как от исключения избавиться.

PS В релизе работает, там похоже нет этой проверки.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038871
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
Для 6 из 100, рекурсивный теперь дает 1с, нерекурсивный 1.2с

Запусти мой all_combinations_6(100)
Код тут 22268813
Интересно сколько времени он у тебя считается.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038940
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
booby, OK посмотрю. У меня растет объем технического долга по топику. Я еще Сашин исходник не смотрел
и не скомпилировал целиком.


вот этот вариант побыстрее будет:
Код: 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.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
type
  TCombination = array of integer;

var
  cnt: 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); //отладка - закомментировать

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

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

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

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

cnt:=0;
GenerateCombinations(100, 6);
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038946
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T, скользкий случай.

Я-бы по возможности по максимуму избегал в итераторах использовать арифметику и сравнение на больше либо равно.
Просто так. Личное предпочтение. Не всегда ведь он вектор. Может быть и список и дерево.

Вот тут описаны категории итераторов https://www.cplusplus.com/reference/iterator/
Может там - больше информации по debug. Возможно в отладке включены дополнительные
asserts которые и спасают код от всяких неосторожных действий в будущем.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038970
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Кодогенерация для С++ в рантайме? .... Хм... эту тему надо вообще толкнуть отдельным топиком.

Насколько я помню тут ключевой вопрос - безопасность. В рантайме ты должен сгенерить неподписанную
никем *.dll -ку и динамически вызвать ее из главной процедуры. Антивирус сойдет с ума.

Это то что легко делается в языках типа JScript/Python/Node/Lisp через eval и то что почти
неизведанно как Антарктида в С++.

Куча софта делает генерацию кода без проблем.
Везде где есть JIT трансляция.
Отдельная DLL нее нужна. Просто выделяешь память, копируешь туда код и помечаешь ее исполняемой.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038972
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Нашел, есть reverse_iterator для перебора в обратном порядке http://www.cplusplus.com/reference/vector/vector/rbegin/

Еле нагуглил как привести reverse_iterator к iterator
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
bool next_combinations(std::vector<int>& a, int max) noexcept {
	for (auto rit = a.rbegin(); rit != a.rend(); rit++, max--) {
		assert(*rit >= 1 && *rit <= max);
		if (*rit < max) {
			(*rit)++;
			for (std::vector<int>::iterator it(rit.base()); it != a.end(); it++) *it = *(it - 1) + 1;
			return true;
		}
	}
	return false;
}


Скорость такая же как с массивом 22268624
Исходник целиком, для компиляции
Код: 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.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <vector>

// Следующее сочетание k элементов диапазона 1...n в массив a[]
// Возвращает true если оно есть
bool next_combinations(std::vector<int>& a, int max) noexcept {
	for (auto rit = a.rbegin(); rit != a.rend(); rit++, max--) {
		assert(*rit >= 1 && *rit <= max);
		if (*rit < max) {
			(*rit)++;
			for (std::vector<int>::iterator it(rit.base()); it != a.end(); it++) *it = *(it - 1) + 1;
			return true;
		}
	}
	return false;
}

//**********************************************
int main(int argc, char**argv)
{
	int k, n;
	if (argc > 2) {
		k = atoi(argv[1]);
		n = atoi(argv[2]);
	} else {
		printf("Too few arguments\n");
		return 0;
	}

	printf("C(%d, %d)\n", k, n);
	std::vector<int> a;
	for (int i = 0; i < k; i++) a.push_back(i + 1);

	int cnt = 0;
	do {
		cnt++;
#ifdef _DEBUG
		for (int i = 0; i < k; i++) printf("%d ", a[i]);
		printf("\n");
#else
		if((cnt & 0x3FFFFFF) == 0) printf("Total: %d\r", cnt);
#endif
	} while (next_combinations(a, n));

	printf("Total: %d\n", cnt);
#if defined(_WIN32) || defined(_WIN64)
	printf("Time %d ms", clock());
#endif
	return 0;
}

...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40038996
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
mayton
Кодогенерация для С++ в рантайме? .... Хм... эту тему надо вообще толкнуть отдельным топиком.

Насколько я помню тут ключевой вопрос - безопасность. В рантайме ты должен сгенерить неподписанную
никем *.dll -ку и динамически вызвать ее из главной процедуры. Антивирус сойдет с ума.

Это то что легко делается в языках типа JScript/Python/Node/Lisp через eval и то что почти
неизведанно как Антарктида в С++.

Куча софта делает генерацию кода без проблем.
Везде где есть JIT трансляция.
Отдельная DLL нее нужна. Просто выделяешь память, копируешь туда код и помечаешь ее исполняемой.

Это офигенская тема. Вы пока тут - не развивайте. Я в пятницу подниму топик.
Но нас будет интересовать JIT для классических компилируемых языков (С++).
Платформеры и так этот жыд имеют.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039008
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
Оптимизировал немного рекурсивный и нерекурсивный алгоритмы.
Для 6 из 100, рекурсивный теперь дает 1с, нерекурсивный 1.2с

Хотел потестить, но не MSVC2019 не хочет компилировать рекурсивный

Код: plaintext
error C3861: '__builtin_expect': identifier not found
на строке
Код: plaintext
1.
if (UNLIKELY(print)) {



если тут написать
Код: plaintext
1.
if (print) {


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

Если закамментить все внутри print_current() то скорость 1.2 сек., против 1.6 сек прошлого варианта.

Но есть подозрение что полный каммент разрешает оптимизатору ломать этот цикл
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
   size_t generate(size_t pos, size_t val, size_t count) noexcept
    {
        const size_t limit = n - k + pos + 1;
        for (; val < limit; val++) {
            curr[pos] = val; // pos не меняется, при pos == last_pos можно за циклом curr[pos] = limit
            if (pos == last_pos) {
                count++; // Можно вынести за цикл как count += limit - val
                print_current();
            } else {
                count = generate(pos + 1, val + 1, count);
            }
        }
        return count;
    }
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039013
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ИМХО в твоем случае, и в моем с фиксированным K 22268813 надо в параметрах передавать указатель на функцию и ее вызывать.
А функций несколько, надо с выводом значений - одна, надо счетчик - другая. Тогда оптимизатор не так сильно будет код ломать.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039025
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Давайте уже подумаем над API использования этого софта.

Как по мне - процесс consumer не сможет заглотить ни STDOT ни vector<vector<int>>. Неудобно это в одном случае.
И не хватит памяти в другом.

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

Как по мне - процесс consumer не сможет заглотить ни STDOT ни vector<vector<int>>. Неудобно это в одном случае.
И не хватит памяти в другом.

Надо думать над умной коммуникацией. Подумайте вот если-бы вы использовали бинарник этого генератора
то каким образом-бы вы забирали результаты этой генерации.


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

Как по мне - процесс consumer не сможет заглотить ни STDOT ни vector<vector<int>>. Неудобно это в одном случае.
И не хватит памяти в другом.

Надо думать над умной коммуникацией. Подумайте вот если-бы вы использовали бинарник этого генератора
то каким образом-бы вы забирали результаты этой генерации.


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

Я думаю о дизайне самой упаковки. Тоесть если вы создали некий модуль (библиотеку) которая генерит
сочетания и выкладываете ее на github или еще куда-то не в виде исходного кода а как бинарник (.dll/.o) + .h
(как это принято в С++) то нужно подумать о том как ее будут использовать.

Один вариант - итератор как сделал Анатолий, размотав стек.

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

на практике наиболее удобным вариантом оказывается использование исходников,
в которых предусмотрены обратные вызовы функций обработки.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039068
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
На сях с бустом есть такое https://www.boost.org/doc/libs/1_70_0/libs/coroutine/doc/html/coroutine/intro.html

А есть в Delphi?
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039072
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton,

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

Например, сочетания по 4 из 6 для множества {0,1,2,3,4,5} выводятся в таком порядке:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
2345
1345
0345
1245
0245
0145
1235
0235
0135
0125
1234
0234
0134
0124
0123



Алгоритм получился довольно быстрым
Код: 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.
procedure GenerateCombinationDesc(p: pIntegerArray; n, k: integer);
var
  len: integer;
begin;
  len:=k;
  dec(k);
  while true do begin;
    dec(n);
    while k>0 do begin;
      p[k]:=n;
      dec(k);
      dec(n);
      end;
    repeat;
      p[0]:=n;
      dec(n);
      //inc(cnt);
      //ShowCombinationDesc(p, len); //обработка сочетания p[0..k-1]
      until n<0;
    repeat;
      inc(k); if k>=len then exit;
      n:=p[k];
      until k<n;
    end;
  end;
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039087
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov, так а в исходном варианте разве не тоже самое?


Код: sql
1.
2.
3.
4.
5.
6.
1 2 3
1 2 4
1 3 4
2 3 4
------
1 2 3 (min)
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039088
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov
mayton,

с бустом ))

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


Код: sql
1.
2.
3.
4.
5.
6.
1 2 3
1 2 4
1 3 4
2 3 4
------
1 2 3 (min)



Не совсем то же самое.
Когда мы наращиваем значения элементов,
то закончим при достижении значения,
которое отличается на дельту (n-k) от индекса элемента.

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

с бустом ))

Ну я не против. Есть ли в Делфях свой буст?


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

Ну я не против. Есть ли в Делфях свой буст?


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

Чорт с ним. Корутитны? Континуэйшены есть?
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039097
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Aleksandr Sharahov
пропущено...


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

Чорт с ним. Корутитны? Континуэйшены есть?


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

Чорт с ним. Корутитны? Континуэйшены есть?


1. Если потребуется, всегда можно эмулировать.
2. Но если кто-то скажет, что у него интерфейс заточен таким образом,
то нафига оно надо, если можно без него.

Твоя реализация - в конечном варианте не-рекурсивная. Поэтому тебе это не надо.
Можно сказать что ты избежал этой участи

Но в топике я также поставил сверх-задачу - обобщить подходы к рекурренным алгоритмам. В частности
к итераторам. Я не буду настаивать. Ведь рекурсия - это сугубо мой интерес. В частности технологические
штуки такие как
  • boost/coroutine (boost)
  • channels (GoLang)
  • delayed lists, streans (MIT-Scheme)
раз я достану из кармана алгоримы на графах и там такой трюк может не проскочить. И размотка
рекурсии в дек или в очередь может быть нетривиальной задачей или просто очень хлопотной
в количестве кода.

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

Твоя реализация - в конечном варианте не-рекурсивная. Поэтому тебе это не надо.
Можно сказать что ты избежал этой участи


Их есть у меня )
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
procedure RecursiveCombination(p: pIntegerArray; n, k: integer);
var
  i: integer;
begin;
  if k<0 then inc(cnt)
  else for i:=k to n do begin;
    p[k]:=i;
    RecursiveCombination(p, i-1, k-1)
    end;
  end;

var
  c: TCombination;
begin;
  cnt:=0;
  SetLength(c, 6);
  RecursiveCombination(@c[0], 100-1, 6-1);
end.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039113
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T

Еле нагуглил как привести reverse_iterator к iterator
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
bool next_combinations(std::vector<int>& a, int max) noexcept {
	for (auto rit = a.rbegin(); rit != a.rend(); rit++, max--) {
		assert(*rit >= 1 && *rit <= max);
		if (*rit < max) {
			(*rit)++;
			for (std::vector<int>::iterator it(rit.base()); it != a.end(); it++) *it = *(it - 1) + 1;
			return true;
		}
	}
	return false;
}


ОК. Ну я скоро запутаюсь в исходниках. Ты еще не создавал репку? У каждого автора уже более чем 1 вариант кода.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039116
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Давайте уже подумаем над API использования этого софта.

Как по мне - процесс consumer не сможет заглотить ни STDOT ни vector<vector<int>>. Неудобно это в одном случае.
И не хватит памяти в другом.

Надо думать над умной коммуникацией. Подумайте вот если-бы вы использовали бинарник этого генератора
то каким образом-бы вы забирали результаты этой генерации.

ИМХО Правильнее получать следующее значение, т.е. есть массив с текущим значением, вызываешь функцию, она внутри превращает массив в следующее, обрабатываешь, получаешь следующее и т.д.
Реализации когда генератор внутри себя выдает значения куда-то там - нечитабельны, т.к. код получается корявый.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039117
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
ОК. Ну я скоро запутаюсь в исходниках. Ты еще не создавал репку? У каждого автора уже более чем 1 вариант кода.

Нормальный этот 22268624 остальные можешь не смотреть.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039126
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Давайте зайдем в "области тьмы". В временую сложность.

Что такое сочетания из 2 по 10 тыщ? Фигня. Щас дам калькулятор.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
scala> def fact(x: BigInt): BigInt = (BigInt(1) to x).foldLeft(BigInt(1))(_ * _)
fact: (x: BigInt)BigInt

scala> def combinations(k: Long, n: Long): BigInt = ( fact(BigInt(n)) / (fact(BigInt(k)) * fact(BigInt(n - k)) ) )
combinations: (k: Long, n: Long)BigInt

scala> combinations(2,10000)
res2: BigInt = 49995000



Это 49 миллионов унылых пар чисел наподобие

Код: sql
1.
2.
3.
4.
1,2
1,3
1,4
9999,10000



и так далее. Как в морском бое. Когда мы заполняем пол-поля треугольником по диагонали.

В топиках простых чисел мы и поболее считывали.

Вот мой concern в чем заключается. Внешний вид алгоритма имеющего квадратичные накладные расходы.

Код: sql
1.
2.
3.
4.
5.
6.
	for (int i = k - 1, max = n; i >= 0; i--, max--) {
		assert(a[i] >= 1 && a[i] <= max);
		if (a[i] < max) {
			a[i]++;
			i++;
			for (; i < k; i++) a[i] = a[i-1] + 1;


Надо поисследовать как ведет себя итеративный алгоритм на малых значениях k при очень больших значениях n.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039127
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Давайте зайдем в "области тьмы". В временую сложность.

Что такое сочетания из 2 по 10 тыщ? Фигня. Щас дам калькулятор.

Это в уме считается, без калькулятора:
Код: plaintext
10000! / (9998! * 2!) = 10000 * 9999 / 2 = 5000 * 9999 = чуть меньше 50 млн.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039128
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да. Этот калькулятор - уродский. Я писал его на коленке.

Код: sql
1.
( fact(BigInt(n)) / (fact(BigInt(k)) * fact(BigInt(n - k)) ) )


Слишком большой числитель и знаменатель при мизерном частном. Соотв надо сократить еще до вычислений факториалы.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039129
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А лямбда похожа на ж0пу

Код: sql
1.
(_ * _)

...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039132
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Правильнее этот 22268468 , он быстр и уродлив одновременно, он быстрее, но непонятней. Запихать его в либу можно, попытаться объяснить кому-то как он работает - сложно.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039134
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
Правильнее этот 22268468 , он быстр и уродлив одновременно, он быстрее, но непонятней. Запихать его в либу можно, попытаться объяснить кому-то как он работает - сложно.

Это-же и есть самый важный скилл. Объяснить ребёнку как оно работает

Пускай внутри коробочки будет Ад и Израиль. А снаружи - розовые единороги.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039145
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Это-же и есть самый важный скилл. Объяснить ребёнку как оно работает

ага, поэтому сначала я родил первый вариант думая от лица процессора, потом переделал в пользу читаемости, потом понял почему второй вариант тормознее, там лишних действий больше, но все-равно я за него.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039165
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T

Запусти мой all_combinations_6(100)
Код тут 22268813
Интересно сколько времени он у тебя считается.

0.6c
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039175
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
думаю, аналог 22269637 на c не сильно отстанет
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039188
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T

добавь ключи -O3 -DNDEBUG

Вот среди ночи я добрался до своего десктопа. Щас посмотрим.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039196
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добавил что-то вроде silent-mode (просто любой третий аргумент командной строки).
Изменил формулу расчета времени. И разделили поток комбинаций и статистику
по раздельным файловым дескрипторам.
Код: 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.
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.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;
}



//**********************************************

int main(int argc, char**argv)
{
    if (argc < 3) {
      fprintf(stderr, "\n  Usage: perm-k-n K N [s]\n\n");
      return 1;
    }
    clock_t start = clock();
    int K = atoi(argv[1]);
    int N = atoi(argv[2]);
    bool silent = argc > 3 ? true : false;
    fprintf(stderr, "C(%d, %d)\n", K,N);
    int a[K];
    for (int i = 0; i < K; i++) a[i] = i + 1;
    int cnt = 0;
    if (silent) {
     fprintf(stderr, "silent_mode = on\n");
     do {
	cnt++;
     } while (nexn_combinations(a, K, N));
    } else {
     do {
	cnt++;
	for(int& i : a) printf("%d ", i);
	printf("\n");
     } while (nexn_combinations(a, K, N));
    }
    fprintf(stderr, "Total: %d Time %.2f s\n", cnt, (float)(clock() - start) / CLOCKS_PER_SEC);
    return 0;



Два из сто тыщ - за 22 секунды.

Код: sql
1.
2.
3.
4.
$ ./perm-k-n.exe 2 100000 s
C(2, 100000)
silent_mode = on
Total: 704982704 Time 22.79 s
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039197
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В исходнике Анатолия такой трюк с silent не прокатывает. Из за рекурсии. В идеале нужно просто написать
два класса с виртуальным методом generate(...) и две реализации с принтом и без.

Вобщем я просто оставлю закоментированным печать векторов сочетаний.

Код: sql
1.
2.
3.
4.
$ /probe-permutation/anatoly-moskovsky$ ./make-run
Time 10.05 s
k=2, n=100000, print=1
total: 4999950000



Пока рекурсия побеждает на длинных N.

Ключи оптимизации и версия компиллятора одинаковые.

Код: sql
1.
2.
$ gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) 
$ uname -a Linux ryzen-ssd 5.8.0-40-generic #45~20.04.1-Ubuntu SMP Fri Jan 15 11:35:04 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux



Скрипт для сборки тоже одинаковый.
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
#!/bin/bash

rm -f perm-k-n.exe

gcc \
 -O3 -DNDEBUG \
 perm-k-n.cpp \
 -o perm-k-n.exe \
 -lstdc++ && ./perm-k-n.exe 2 100000
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039198
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вот это число надо проверить. Может баг?
mayton

Код: sql
1.
Total: 704982704 Time 22.79 s

...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039207
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Вот это число надо проверить. Может баг?
mayton

Код: sql
1.
Total: 704982704 Time 22.79 s


Ты за разрядность int вылез ))
Поправь тип счетчика
Код: plaintext
1.
2.
3.
int64_t cnt = 0;
...
printf("Total: %lld\n", cnt);



mayton
Код: plaintext
1.
2.
3.
4.
5.
...
    int K = atoi(argv[1]);
...
    int a[K];
...


У тебя это скомпилировалось?
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039220
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039227
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
 long int cnt = 0;
    if (silent) {
     fprintf(stderr, "silent_mode = on\n");
     do {
	cnt++;
     } while (nexn_combinations(a, K, N));
    } else {
     do {
	cnt++;
	for(int& i : a) printf("%d ", i);
	printf("\n");
     } while (nexn_combinations(a, K, N));
    }
 fprintf(stderr, "Total: %ld Time %.2f s\n", cnt, (float)(clock() - start) / CLOCKS_PER_SEC);



Вот так корректно для GCC.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039232
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Код: plaintext
1.
fprintf(stderr, "Total: %ld Time %.2f s\n", cnt, (float)(clock() - start) / CLOCKS_PER_SEC);


clock() считает с момента запуска приложения, т.е. у тебя start == 0, можно его выкинуть.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039236
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пускай будет. На результат почти не влияет.

А если будет пользовательский ввод - то корректнее считать от начала алгоритма а не процедуры main.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039237
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Да.

Это динамические (или как их там правильно) массивы на стэке, в стандарте это есть, а MS еще не реализовал, поэтому у меня не компилируется.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039248
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Прикинь я и не заметил. Еще походу удивился. Где там new ?
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039273
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Ну я скоро запутаюсь в исходниках.


Все мои предыдущие исходники отправляются в корзину.
Вот этот генерирует 6 из 100 на i7-7700 за 453 msec:
Код: 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.
function GenerateCombinationDesc(p: pIntegerArray; n, k: integer): integer;
var
  i: integer;
begin;
  Result:=0;
  i:=k;
  dec(i);
  while true do begin;
    while i>0 do begin;
      dec(n);
      p[i]:=n;
      dec(i);
      end;
    repeat;
      dec(n);
      p[0]:=n;
      inc(Result);                  //подсчет сочетаний
      //ShowCombinationDesc(p, k);  //обработка очередного сочетания
      until n<=0;
    repeat;
      inc(i); if i>=k then exit;
      n:=p[i];
      until i<n;
    end;
  end;



как вызывать:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
procedure ShowCombinationDesc(p: pIntegerArray; len: integer);
var
  s: AnsiString;
  i: integer;
begin;
  for i:=0 to len-1 do s:=s+AnsiChar(Ord('0')+p[i]);
  //Form1.Memo1.Lines.Add(s);
  WriteLn(s);
  end;

var
  n, k: integer;
begin;
  n:=100; k:=6; 
  SetLength(c, k);
  cnt:=GenerateCombinationDesc(@c[0], n, k);
end.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039285
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov

Все мои предыдущие исходники отправляются в корзину.

это прекрасно. Вот что брейншторм делает!

Сегодня вечером попробую собрать на fpc.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039553
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я вот думаю можно ли как-то произвольное преобразование сделать?
Т.е. знаешь номер по-порядку и преобразуешь его в соответствующее ему сочетание и наоборот. Типа как перевод из одной системы счисления в другую.
По идее это тоже разновидность системы счисления, но разные разряды имеют разный вес.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039560
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
Я вот думаю можно ли как-то произвольное преобразование сделать?
Т.е. знаешь номер по-порядку и преобразуешь его в соответствующее ему сочетание и наоборот. Типа как перевод из одной системы счисления в другую.
По идее это тоже разновидность системы счисления, но разные разряды имеют разный вес.


1. В английской вики приводятся варианты нумерации, фактически имитирующие генерацию.
2. Можно старое сочетание использовать для генерации нового.
3. Можно рассмотреть комбинацию 1 и 2 - несплошную нумерацию,
когда у номера комбинации единичные биты соответствуют
выбранным элементам множества.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039570
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
Я вот думаю можно ли как-то произвольное преобразование сделать?
Т.е. знаешь номер по-порядку и преобразуешь его в соответствующее ему сочетание и наоборот. Типа как перевод из одной системы счисления в другую.
По идее это тоже разновидность системы счисления, но разные разряды имеют разный вес.

Можно для все последовательности посчитать каждый 1000-й элемент. И назвать его опорным.
И положить в табличку. Порядковый номер => опорный элемент. И дальше если нужно например
100000-й сочетание - то ищем бинарным поиском по табличке порядковый номер. И потом опорный
и дальше перемоткой вперед добиваем столько позиций сколько надо.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039926
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
UP. Привет всем.

Прошу прощения за отсутсвие. Я помню что обещал. Просто неделька была бешеная. ПОЦ-ы. Стартапы.
Тоесть POC-s. Я только-только отбился от всех.

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

Почитал и ничего не понял если честно, еще раз почитаю
Aleksandr Sharahov
2. Можно старое сочетание использовать для генерации нового.

У меня в коде так и происходит. next_combinations() получает на вход массив с текущей комбинацией и преобразует ее в следующую.

mayton
Можно для все последовательности посчитать каждый 1000-й элемент. И назвать его опорным.
И положить в табличку. Порядковый номер => опорный элемент. И дальше если нужно например
100000-й сочетание - то ищем бинарным поиском по табличке порядковый номер. И потом опорный
и дальше перемоткой вперед добиваем столько позиций сколько надо.

Можно, эдакие радужные таблицы получатся, но их считать надо или хранить.

Я пытался в уме формулу вывести для небольшой последовательности типа С(2,5) для 2-х вроде должно получиться, а если С(3,5) то тут уже сложнее, надо поизучать в экселе.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40039940
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T, кстати идеи которые у нас звучали в топике простых чисел
применимы и для последовательности сочетаний.

Например такое

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
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



хорошо сжимается дифференциальным кодированием или деревом префиксов.

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

Я синтаксис программы не помню. Кажется она начинается с program и заканчивается end.

Сделал так.

Код: 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.
program perm_k_n;

function GenerateCombinationDesc(p: pIntegerArray; n, k: integer): integer;
var
  i: integer;
begin;
  Result:=0;
  i:=k;
  dec(i);
  while true do begin;
    while i>0 do begin;
      dec(n);
      p[i]:=n;
      dec(i);
      end;
    repeat;
      dec(n);
      p[0]:=n;
      inc(Result);                  //подсчет сочетаний
      //ShowCombinationDesc(p, k);  //обработка очередного сочетания
      until n<=0;
    repeat;
      inc(i); if i>=k then exit;
      n:=p[i];
      until i<n;
    end;
  end;

procedure ShowCombinationDesc(p: pIntegerArray; len: integer);
var
  s: AnsiString;
  i: integer;
begin;
  for i:=0 to len-1 do s:=s+AnsiChar(Ord('0')+p[i]);
  //Form1.Memo1.Lines.Add(s);
  WriteLn(s);
  end;

var
  n, k: integer;
begin;
  n:=100; k:=6; 
  SetLength(c, k);
  cnt:=GenerateCombinationDesc(@c[0], n, k);
end.

ens.



Сборочный скриптик.

Код: sql
1.
2.
3.
4.
5.
6.
#!/bin/bash

rm -f perm-k-n.exe

fpc -CX -O3 -XX -vewnhi -Fi. -Fu. -FU. perm-k-n.lpr \
  && mv perm-k-n perm-k-n.exe



И лог ошибок. Кто знает как исправить?

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
Hint: Start of reading config file /etc/fpc.cfg
Hint: End of reading config file /etc/fpc.cfg
Free Pascal Compiler version 3.0.4+dfsg-23 [2019/11/25] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling perm-k-n.lpr
perm-k-n.lpr(7,3) Error: Identifier not found "Result"
perm-k-n.lpr(13,13) Error: Incompatible types: got "SmallInt" expected "IntegerArray"
perm-k-n.lpr(18,13) Error: Incompatible types: got "SmallInt" expected "IntegerArray"
perm-k-n.lpr(19,11) Error: Identifier not found "Result"
perm-k-n.lpr(24,11) Error: Incompatible types: got "IntegerArray" expected "SmallInt"
perm-k-n.lpr(34,46) Error: Operator is not overloaded: "Byte" + "IntegerArray"
perm-k-n.lpr(43,13) Error: Identifier not found "c"
perm-k-n.lpr(44,3) Error: Identifier not found "cnt"
perm-k-n.lpr(44,33) Error: Identifier not found "c"
perm-k-n.lpr(47,1) Fatal: There were 9 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040005
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.
program perm_k_n;

{$APPTYPE CONSOLE}

uses
  SysUtils;

procedure ShowCombinationDesc(p: pIntegerArray; len: integer);
var
  s: AnsiString;
  i: integer;
begin;
  for i:=0 to len-1 do s:=s+AnsiChar(Ord('0')+p[i]);
  WriteLn(s);
  end;

function GenerateCombinationDesc(p: pIntegerArray; n, k: integer): integer;
var
  i: integer;
begin;
  Result:=0;
  i:=k;
  dec(i);
  while true do begin;
    while i>0 do begin;
      dec(n);
      p[i]:=n;
      dec(i);
      end;
    repeat;
      dec(n);
      p[0]:=n;
      inc(Result);                  //подсчет сочетаний
      //ShowCombinationDesc(p, k);  //обработка очередного сочетания
      until n<=0;
    repeat;
      inc(i); if i>=k then exit;
      n:=p[i];
      until i<n;
    end;
  end;

var
  c: array of integer;
  n, k, cnt: integer;
begin;
  n:=100; k:=6;
  //n:=6; k:=4;
  SetLength(c, k);
  cnt:=GenerateCombinationDesc(@c[0], n, k);
  WriteLn(cnt);
  ReadLn
end.

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

В идеале не должно быть ничего из упомянутого. Надо просто формулу для перевода какого-то значения, например 12345, в счисление C(K,N) и обратно
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040011
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T
mayton
хорошо сжимается дифференциальным кодированием или деревом префиксов.

В идеале не должно быть ничего из упомянутого. Надо просто формулу для перевода какого-то значения, например 12345, в счисление C(K,N) и обратно


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

Все таки разница между делфи и фпс большая

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
Hint: Start of reading config file /etc/fpc.cfg
Hint: End of reading config file /etc/fpc.cfg
Free Pascal Compiler version 3.0.4+dfsg-23 [2019/11/25] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling perm-k-n.lpr
perm-k-n.lpr(3,2) Warning: APPTYPE is not supported by the target OS
perm-k-n.lpr(13,46) Error: Operator is not overloaded: "Byte" + "IntegerArray"
perm-k-n.lpr(21,3) Error: Identifier not found "Result"
perm-k-n.lpr(27,13) Error: Incompatible types: got "SmallInt" expected "IntegerArray"
perm-k-n.lpr(32,13) Error: Incompatible types: got "SmallInt" expected "IntegerArray"
perm-k-n.lpr(33,11) Error: Identifier not found "Result"
perm-k-n.lpr(38,11) Error: Incompatible types: got "IntegerArray" expected "SmallInt"
perm-k-n.lpr(54) Fatal: There were 6 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040020
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton,

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

{$APPTYPE CONSOLE} надо убрать

нижний блок можно попробовать заменить на
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
var
  c: array of integer;
  p: pointer;           
  n, k, cnt: integer;
begin;
  n:=100; k:=6;
  SetLength(c, k);
  p:=@c[0];
  cnt:=GenerateCombinationDesc(p, n, k);
  WriteLn(cnt);
  ReadLn;
end.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040112
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Из любопытства вопрос, удельную скорость есть смысл сопоставить? К примеру кол-во на 1 ГГц процессора или ядра, на 1 ГБ памяти... Различия в операционках за кадром.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040121
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov, уже лучше.

Код: 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.
program perm_k_n;

uses
  SysUtils;

procedure ShowCombinationDesc(p: pIntegerArray; len: integer);
var
  s: AnsiString;
  i: integer;
begin;
  for i:=0 to len-1 do s:=s+AnsiChar(Ord('0')+p[i]);
  WriteLn(s);
  end;

function GenerateCombinationDesc(p: pIntegerArray; n, k: integer): integer;
var
  i: integer;
  Result: integer;
begin;
  Result:=0;
  i:=k;
  dec(i);
  while true do begin;
    while i>0 do begin;
      dec(n);
      p[i]:=n;
      dec(i);
      end;
    repeat;
      dec(n);
      p[0]:=n;
      inc(Result);                  //подсчет сочетаний
      //ShowCombinationDesc(p, k);  //обработка очередного сочетания
      until n<=0;
    repeat;
      inc(i); if i>=k then exit;
      n:=p[i];
      until i<n;
  end;
end;

var
  c: array of integer;
  p: pointer;           
  n, k, cnt: integer;
begin;
  n:=100; k:=6;
  SetLength(c, k);
  p:=@c[0];
  cnt:=GenerateCombinationDesc(p, n, k);
  WriteLn(cnt);
  ReadLn;
end.



Остались ошибки типизации.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
Hint: Start of reading config file /etc/fpc.cfg
Hint: End of reading config file /etc/fpc.cfg
Free Pascal Compiler version 3.0.4+dfsg-23 [2019/11/25] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling perm-k-n.lpr
perm-k-n.lpr(11,46) Error: Operator is not overloaded: "Byte" + "IntegerArray"
perm-k-n.lpr(26,13) Error: Incompatible types: got "SmallInt" expected "IntegerArray"
perm-k-n.lpr(31,13) Error: Incompatible types: got "SmallInt" expected "IntegerArray"
perm-k-n.lpr(37,11) Error: Incompatible types: got "IntegerArray" expected "SmallInt"
perm-k-n.lpr(53,4) Fatal: There were 4 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040128
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98
Из любопытства вопрос, удельную скорость есть смысл сопоставить? К примеру кол-во на 1 ГГц процессора или ядра, на 1 ГБ памяти... Различия в операционках за кадром.


количество тактов на сочетание сильно зависит от количества элементов в сочетании, у меня так:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
из 180 по 5: 1488847536 за 547 мсек
1,32 тактов

из 100 по 6: 1192052400 за 453 мсек
1,37 тактов

из 64 по 7: 621216192 за 234 мсек
1,36 тактов

из 32 по 16: 601080390 за 609 мсек
3,65 тактов

из 36 по 24: 1251677700 за 2156 мсек
6,20 тактов
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040130
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.
program perm_k_n;

uses
  SysUtils;

procedure ShowCombinationDesc(p: pIntegerArray; len: integer);
var
  s: AnsiString;
  i: integer;
begin;
  for i:=0 to len-1 do s:=s+AnsiChar(Ord('0')+byte(p^[i]));
  WriteLn(s);
  end;

function GenerateCombinationDesc(p: pIntegerArray; n, k: integer): integer;
var
  i: integer;
begin;
  Result:=0;
  i:=k;
  dec(i);
  while true do begin;
    while i>0 do begin;
      dec(n);
      p^[i]:=n;
      dec(i);
      end;
    repeat;
      dec(n);
      p^[0]:=n;
      inc(Result);                  //подсчет сочетаний
      //ShowCombinationDesc(p, k);  //обработка очередного сочетания
      until n<=0;
    repeat;
      inc(i); if i>=k then exit;
      n:=p^[i];
      until i<n;
    end;
  end;

var
  c: array of integer;
  p: pointer;
  n, k, cnt: integer;
begin;
  n:=100; k:=6;
  SetLength(c, k);
  p:=@c[0];
  cnt:=GenerateCombinationDesc(p, n, k);
  WriteLn(cnt);
  ReadLn;
end.

...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040131
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А прога знает, что (37 по 24) == (37 по 13) ?
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040132
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98
А прога знает, что (37 по 24) == (37 по 13) ?


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

Код: sql
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.
program perm_k_n;

uses
  SysUtils;

procedure ShowCombinationDesc(p: pIntegerArray; len: integer);
var
  s: AnsiString;
  i: integer;
begin;
  for i:=0 to len-1 do s := s + AnsiChar(Ord('0') + byte(p^[i]));
  WriteLn(s);
end;

function GenerateCombinationDesc(p: pIntegerArray; n, k: integer): integer;
var
  i: integer;
  Result: Integer;
begin;
  Result:=0;
  i:=k;
  dec(i);
  while true do begin;
    while i>0 do begin;
      dec(n);
      p^[i]:=n;
      dec(i);
    end;
    repeat;
      dec(n);
      p^[0]:=n;
      inc(Result);                  //подсчет сочетаний
      ShowCombinationDesc(p, k);  //обработка очередного сочетания
    until n<=0;
    repeat;
      inc(i); if i>=k then exit;
      n:=p^[i];
    until i<n;
  end;
end;

var
  c: array of integer;
  p: pointer;
  n, k, cnt: integer;
begin;
  n:=6; k:=5;
  SetLength(c, k);
  p:=@c[0];
  cnt:=GenerateCombinationDesc(p, n, k);
  WriteLn(cnt);
end.



Отчот для C(5,6)
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
sharahov$ ./perm-k-n.exe 
12345
02345
01345
01245
01235
01234
-20104
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040150
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
надо присвоить результат значению функции
Код: 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.
function GenerateCombinationDesc(p: pIntegerArray; n, k: integer): integer;
var
  i: integer;
  Result: Integer;
label
  Done;
begin;
  Result:=0;
  i:=k;
  dec(i);
  while true do begin;
    while i>0 do begin;
      dec(n);
      p^[i]:=n;
      dec(i);
    end;
    repeat;
      dec(n);
      p^[0]:=n;
      inc(Result);                  //подсчет сочетаний
      ShowCombinationDesc(p, k);  //обработка очередного сочетания
    until n<=0;
    repeat;
      inc(i); if i>=k then goto Done;
      n:=p^[i];
    until i<n;
  end;
Done:
  GenerateCombinationDesc:=Result;
end;
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040164
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov, уже работает но счетчик где-то циклически переполняется .

При

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
var
  c: array of integer;
  p: pointer;
  n, k, cnt: integer;
begin;
  n:=200; k:=2;
  SetLength(c, k);
  p:=@c[0];
  cnt:=GenerateCombinationDesc(p, n, k);
  WriteLn(cnt);
end.



Код: sql
1.
19900



При
Код: sql
1.
n:=400; k:=2;



Результат
Код: sql
1.
14264



А при n=600, результат -16908 (отрицательный)

А так по сорости быстро. Да. Очень быстро. Но надо фиксить.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040165
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Aleksandr Sharahov, уже работает но счетчик где-то циклически переполняется .
А при n=600, результат -16908 (отрицательный)


А k какое? Может, результат укладывается не укладывается в 32-битный integer?

попробуй заменить его на int32 или longint

пишут есть еще delphi mode: https://wiki.freepascal.org/Integer
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040204
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov, если переключать в Delphi mode ({$mode DelphiUnicode}) то
компилляция останавливается. Еще новые ошибки выдает. Видимо уровни строгости выше.

Вобщем заменил Integer у которого разрядная сетка плавает (16-32 бит) на более строгий Int32
и пересобрал.

Вот последняя версия. Работает быстро. 2 из 10 000 выдает за милисекунды. Точно померять
не могу т.к. не знаю функций точных часов.

Вот последнний исходник.

Код: 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.
54.
55.
56.
program perm_k_n;

uses
  SysUtils;

procedure ShowCombinationDesc(p: pIntegerArray; len: Int32);
var
  s: AnsiString;
  i: Int32;
begin;
  for i:=0 to len-1 do s := s + AnsiChar(Ord('0') + byte(p^[i]));
  WriteLn(s);
end;

function GenerateCombinationDesc(p: pIntegerArray; n, k: Int32): Int32;
var
  i: Int32;
  Result: Int32;
label
Done;
begin;
  Result:=0;
  i:=k;
  dec(i);
  while true do begin;
    while i>0 do begin;
      dec(n);
      p^[i]:=n;
      dec(i);
    end;
    repeat;
      dec(n);
      p^[0]:=n;
      inc(Result);                  //подсчет сочетаний
      //ShowCombinationDesc(p, k);  //обработка очередного сочетания
    until n<=0;
    repeat;
      inc(i); if i>=k then goto Done;
      n:=p^[i];
    until i<n;
  end;
  Done:
  GenerateCombinationDesc:=Result;
end;

var
  c: array of Int32;
  p: pointer;
  n, k, cnt: Int32;
begin;
  n:=10000; k:=2;
  SetLength(c, k);
  p:=@c[0];
  cnt:=GenerateCombinationDesc(p, n, k);
  WriteLn(cnt);
end.


...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040220
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98
А прога знает, что (37 по 24) == (37 по 13) ?

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

также имеет смысл заменить pIntegerArray на PInt32Array
и после uses добавить определения своих массивов:
Код: pascal
1.
2.
3.
type
  Int32Array  = array[0..$effffff] of Int32;
  PInt32Array = ^Int32Array;
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040311
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T, а ты сравни у него близкие значения
авториз 32 по 16: 601080390 за 609 мсек
3,65 тактов

из 36 по 24: 1251677700 за 2156 мсек
6,20 тактов Разница в 2 раза. По проге посмотреть (может ошибаюсь), цикл в цикле, затраты n*k.
Считаем для k=n-k, а выход только преобразуем в к=к. Я так подумал. Особенно когда (1000, 800) против (1000, 200)
Нешто это съест економию? А так надо писать памятку по применению. Чтоб прогер перед вызовом и после сам по желанию преобразовывал туда-сюда.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040318
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98
Dima T, а ты сравни у него близкие значения
авториз 32 по 16: 601080390 за 609 мсек
3,65 тактов

из 36 по 24: 1251677700 за 2156 мсек
6,20 тактов
Разница в 2 раза. По проге посмотреть (может ошибаюсь), цикл в цикле, затраты n*k.
Считаем для k=n-k, а выход только преобразуем в к=к. Я так подумал. Особенно когда (1000, 800) против (1000, 200)
Нешто это съест економию? А так надо писать памятку по применению. Чтоб прогер перед вызовом и после сам по желанию преобразовывал туда-сюда.

не все прогеры даже станут преобразовывать,
некоторые могут додуматься вместо элементов, вошедших в сочетание,
работать с невошедшими элементами
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040485
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98
Dima T, а ты сравни у него близкие значения
авториз 32 по 16: 601080390 за 609 мсек
3,65 тактов

из 36 по 24: 1251677700 за 2156 мсек
6,20 тактов
Разница в 2 раза. По проге посмотреть (может ошибаюсь), цикл в цикле, затраты n*k.
Считаем для k=n-k, а выход только преобразуем в к=к. Я так подумал. Особенно когда (1000, 800) против (1000, 200)
Нешто это съест економию? А так надо писать памятку по применению. Чтоб прогер перед вызовом и после сам по желанию преобразовывал туда-сюда.
Это не близкие, надо считать сколько разрядов было задействовано для получение следующего значения. Например С(3,6)
ЗначениеРазрядов1 2 3 41 2 3 511 2 3 611 2 4 521 2 4 621 2 5 621 3 4 531 3 4 611 3 5 62...
Поменять может потребоваться от 1 до k разрядов. Назовем проверку разряда элементарной операцией.
Уже видно что в среднем будет менее k/2 элементарных операций.

Для преобразования от k к n-k надо перебрать все n и выкинуть из них все что есть с исходной выборке. В один проход делается, но это примерно n+k элементарных операций, т.е. все равно это будет дольше чем (n-k)/2. Как-то так.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040487
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov

Код: pascal
1.
2.
type
  Int32Array  = array[0..$effffff] of Int32;


Саша а что эта форма объявления массива означает? Бесконечный массив? Или до effffff включительно?
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040514
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Aleksandr Sharahov

Код: pascal
1.
2.
type
  Int32Array  = array[0..$effffff] of Int32;


Саша а что эта форма объявления массива означает? Бесконечный массив? Или до effffff включительно?


Формально, это массив от 0 до максимального значения, которое позволяет компилятор (effffff).
Но это всего лишь определение типа, т.е. реально память не выделяется, мы здесь просто говорим,
что с так объявленной памятью хотим работать как с массивом целых максимальной длины,
т.е. после целого с индексом 0 может быть расположено еще сколько угодно целых.

Понятно, что такой огромный массив нам не нужен, вся фишка в следующем определении
Код: pascal
1.
PInt32Array = ^Int32Array;


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

Обычно для статических массивов компилятор Delphi строит более эффективный код, чем для динамических массивов.
Поэтому память мы выделяем динамически вызовом SetLength, а работаем с ней как со статическим массивом на стеке.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040534
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Понятно.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040548
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov
Формально, это массив от 0 до максимального значения, ........ как со статическим массивом на стеке.
Ты книги должен писать по дельфе.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040551
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov
некоторые могут додуматься вместо элементов, вошедших в сочетание,
работать с невошедшими элементами
А можно, чтобы прога додумывалась до этого сама? Ну-у, чтобы это без больших усилий прогера. Или, если надо тупо гнать массу сочетаний, то выдавать сразу взаимодополняемыми парами сочетаний?
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040564
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98
Aleksandr Sharahov
некоторые могут додуматься вместо элементов, вошедших в сочетание,
работать с невошедшими элементами
А можно, чтобы прога додумывалась до этого сама? Ну-у, чтобы это без больших усилий прогера. Или, если надо тупо гнать массу сочетаний, то выдавать сразу взаимодополняемыми парами сочетаний?


Можно, но немного допрограммировать придется.

Способ 1.
Если n небольшое, например n<=32 (или 64), сочетание можно представить целым числом,
биты которого равны 1 - если элемент входит в сочетание, 0 - если не входит.
Остается на входе выбрать меньшее k, а на выходе инвертировать сочетание (xor -1)

Способ 2.
Сначала ответим на вопрос: зачем вообще в нашей программе понадобились сочетания?
Ведь это не конечный результат работы, надо потом с ними что-то делать.
Далее: как это что-то мы будем делать?
Скорее всего, перебирая в цикле элементы, *вошедшие* в сочетание.
И что мешает нам как программистам предусмотреть цикл для *невошедших* элементов?
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040601
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Спос1 понятно.
Спос2: Дима полагает, что ускорения не достичь.

Dima T
Для преобразования от k к n-k надо перебрать все n и выкинуть из них все что есть с исходной выборке. В один проход делается, но это примерно n+k элементарных операций,
Вот с этим я не согласен. Они ведь упорядочены. Сравнил удачно и передвинул указатель к след. (n + o(k)) Операций.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040626
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98
Dima T
Для преобразования от k к n-k надо перебрать все n и выкинуть из них все что есть с исходной выборке. В один проход делается, но это примерно n+k элементарных операций,
Вот с этим я не согласен. Они ведь упорядочены. Сравнил удачно и передвинул указатель к след. (n + o(k)) Операций.

Благодаря тому что упорядочены можно преобразовать в один проход, но надо пройтись по всем элементам обоих массивов (n+k), когда для получения следующего значения надо поработать с менее k/2 элементов, даже если k максимально (n-1), то это всего лишь (n-1)/2
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040692
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98
Спос1 понятно.
Спос2: Дима полагает, что ускорения не достичь.

Dima T
Для преобразования от k к n-k надо перебрать все n и выкинуть из них все что есть с исходной выборке. В один проход делается, но это примерно n+k элементарных операций,
Вот с этим я не согласен. Они ведь упорядочены. Сравнил удачно и передвинул указатель к след. (n + o(k)) Операций.


Ответ на этот вопрос и ожидаемый, и удивительный.
Если коротко, Dima T прав. Удивительно, как сильно он прав )

Даже если реализовать второй способ по уму, и вообще не формировать новое сочетание,
а обрабатывать элементы "на лету", то все равно n лишних сравнений для каждого сочетания
вносят замедление, которое значительно перевешивает полученный при генерации выигрыш.

Проверку я проводил на том же примере 24 из 36.
Обработка сочетаний заключалась в вычислении общей суммы всех элементов сочетаний по модулю 2^32.
"Оптимизированный" вариант, который вычислял сумму элементов, не вошедших в сочетания 12 из 36,
оказался в 2 раза медленнее.

Вывод.
Выгоднее сразу генерировать сочетания, которые не требуется "переделывать" перед обработкой.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040728
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Генерацию длинных сочетаний можно оптимизировать за счет более быстрого формирования хвоста
в случае, когда он состоит из серии последовательных элементов.
Например, для каждого из следующих переходов достаточно изменить 1 элемент:
02348
01348
01248
01238

Длинные сочетания следующий алгоритм на Delphi генерирует быстрее предыдущего более, чем в 2 pаза:
Код: 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.
function GenerateCombinationDesc2(p: pIntegerArray; n, k: integer): integer;
var
  i: integer;
begin;
  Result:=0;
  i:=k;
  while true do begin;
    dec(i);
    while i>0 do begin;
      dec(n);
      p[i]:=n;
      dec(i);
      end;
    repeat;
      dec(n);
      p[0]:=n;
      inc(Result);                  //подсчет сочетаний
      //ShowCombinationDesc(p, k);  //обработка очередного сочетания
      until n<=0;
    while true do begin;
      repeat;
        inc(i); if i>=k then exit;
        n:=p[i];
        until i<n;
      dec(n);
      p[i]:=n;
      if i<n then break;
      inc(Result);                  //подсчет сочетаний
      //ShowCombinationDesc(p, k);  //обработка очередного сочетания
      end;
    end;
  end;
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040845
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
С псевдооптимизациями я понял.

Теперь вопрос по способам использования. Данная версия заточена на массовый стрим сочетаний.
А если хочется ... не уверен, что правильно, реэнтерабельности?
Аналогично next_permutation()
Сильно надо курочить для переделки под сохранение последнего состояния циклов?
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40040848
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98
А если хочется ... не уверен, что правильно, реэнтерабельности?
Аналогично next_permutation()

22268624
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40043847
__Avenger__
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Мой вариант:

Код: 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.
program TestGenComb;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

procedure Combination(const AElement: TArray<Integer>; const ALen, AStartPos: Integer; var AResult: TArray<Integer>; const ALenElement, ALenResult: Integer; var ACount: Integer); inline;
var
  Index: Integer;
  ANum: Integer;
begin
  if ALen < 1 then
  begin
    Inc(ACount);
    for ANum in AResult do
      Write(ANum, ' ');
    WriteLn;
    Exit;
  end;
  for Index := AStartPos to ALenElement - ALen do
  begin
    AResult[ALenResult - ALen] := AElement[Index];
    Combination(AElement, ALen - 1, Index + 1, AResult, ALenElement, ALenResult, ACount);
  end;
end;

var
  FElement,
  FResult: TArray<Integer>;
  FCount, I: Integer;

begin
  try
    FCount := 0;
    SetLength(FElement, 10);
    for I := Low(FElement) to High(FElement) do
      FElement[I] := I + 1;

    SetLength(FResult, 6);
    Combination(FElement, Length(FResult), 0, FResult, Length(FElement), Length(FResult), FCount);

    WriteLn(FCount);
    ReadLn;
  except
    on E: Exception do
      WriteLn(E.ClassName, ': ', E.Message);
  end;
end.

...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40043851
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пора наверное делать сравнительную табличку.
Все реализации. И скорость генерации.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40043888
__Avenger__
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov
Генерацию длинных сочетаний можно оптимизировать за счет более быстрого формирования хвоста
в случае, когда он состоит из серии последовательных элементов.
Например, для каждого из следующих переходов достаточно изменить 1 элемент:
02348
01348
01248
01238

Длинные сочетания следующий алгоритм на Delphi генерирует быстрее предыдущего более, чем в 2 pаза:
Код: 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.
function GenerateCombinationDesc2(p: pIntegerArray; n, k: integer): integer;
var
  i: integer;
begin;
  Result:=0;
  i:=k;
  while true do begin;
    dec(i);
    while i>0 do begin;
      dec(n);
      p[i]:=n;
      dec(i);
      end;
    repeat;
      dec(n);
      p[0]:=n;
      inc(Result);                  //подсчет сочетаний
      //ShowCombinationDesc(p, k);  //обработка очередного сочетания
      until n<=0;
    while true do begin;
      repeat;
        inc(i); if i>=k then exit;
        n:=p[i];
        until i<n;
      dec(n);
      p[i]:=n;
      if i<n then break;
      inc(Result);                  //подсчет сочетаний
      //ShowCombinationDesc(p, k);  //обработка очередного сочетания
      end;
    end;
  end;



Ваш алгоритм делает перестановки не из чисел массива, а просто уменьшая и увеличивая начальные числа на 1. Это неправильно, должны быть перестановки элементов массива.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40043899
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
__Avenger__
Ваш алгоритм делает перестановки не из чисел массива, а просто уменьшая и увеличивая начальные числа на 1. Это неправильно, должны быть перестановки элементов массива.

Все верно, так получилось что исходно все решали задачу перестановок диапазона от 1 до N, где N задается на входе.
Будем считать эти числа индексами элементов массива начиная с 1.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40044843
Gennadiy Usov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Здесь на топике рассматриваются только перестановки N чисел из M чисел?

А если ещё рассматривать все комбинации чисел из M чисел:
по 1, по 2, по 3 и т.д. за один прогон
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40044846
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Gennadiy Usov
А если ещё рассматривать все комбинации чисел из M чисел:
по 1, по 2, по 3 и т.д. за один прогон

Если еще добавить используемое тут условие монотонного возрастания, то такая комбинация одна для каждого М.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40044850
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Gennadiy Usov
Здесь на топике рассматриваются только перестановки N чисел из M чисел?

А если ещё рассматривать все комбинации чисел из M чисел:
по 1, по 2, по 3 и т.д. за один прогон


тогда просто M-битный счетчик: K=K+1
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40044851
Gennadiy Usov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Кстати, если найдена одна комбинация N из М, то тут же есть одна комбинация (М - N) из М

Или проще (и быстрее) в едином цикле (циклах)
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40044935
Gennadiy Usov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
нашел в Питоне подпрограмму:

itertools.combinations(iterable, [r]) - комбинации длиной r из iterable без повторяющихся элементов.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40044940
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Gennadiy Usov
нашел в Питоне подпрограмму:

itertools.combinations(iterable, [r]) - комбинации длиной r из iterable без повторяющихся элементов.

Нам тут надо чуть сложнее, комбинация должна быть монотонно возрастающая, т.е. надо {1,2,3} и не надо {1,3,2}, {3,2,1} и т.п.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40044943
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Обсуждать питонские комбинаторщики имеет смысл только с их исходным кодом и если они несут идею лучше
чем мы уже здесь рассмотрели.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40044945
Gennadiy Usov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
mayton
Обсуждать питонские комбинаторщики имеет смысл только с их исходным кодом
и если они несут идею лучше чем мы уже здесь рассмотрели.
Для оценочных вычислений на небольших массивах удобно.

Массив из 25 чисел обрабатывается за 5 сек. на моём компе.
33554430 вариантов.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40044946
Gennadiy Usov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dima T
Gennadiy Usov
нашел в Питоне подпрограмму:
itertools.combinations(iterable, [r]) - комбинации длиной r из iterable без повторяющихся элементов.

Нам тут надо чуть сложнее, комбинация должна быть монотонно возрастающая,
т.е. надо {1,2,3} и не надо {1,3,2}, {3,2,1} и т.п.
Если исходный массив монотонно возрастающий (убывающий),
то и получаемые массивы будут то же монотонно возрастающие (убывающие)

Проверено
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40044947
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Без исходников - неинтересно.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40045190
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вот код реализации itertools.combination: https://github.com/python/cpython/blob/master/Modules/itertoolsmodule.c#L2674
Алгоритм похож на 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.
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.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
#include <iostream>
#include <vector>

#define LIKELY(x)       __builtin_expect(!!(x), 1)
#define UNLIKELY(x)     __builtin_expect(!!(x), 0)


class PythonCombinator
{
public:
    PythonCombinator(size_t k, std::vector<int> items, bool print)
        : k(k)
        , n(items.size())
        , items(std::move(items))
        , print(print)
    {
        curr.resize(k);
        for (size_t i = 0; i < k; i++) {
            curr[i] = i;
        }
    }

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

private:
    // алгоритм
    bool generate_one() noexcept
    {
        ssize_t i, j;
        /* Scan indices right-to-left until finding one that is not
           at its maximum (i + n - r). */
        for (i = k - 1; i >= 0 && curr[i] == i + n - k; i--)
            ;

        /* If i is negative, then the indices are all at
           their maximum value and we're done. */
        if (i < 0)
            return false;

        /* Increment the current index which we know is not at its
           maximum.  Then move back to the right setting each index
           to its lowest possible value (one higher than the index
           to its left -- this maintains the sort order invariant). */
        curr[i]++;
        for (j = i + 1; j < k; j++)
            curr[j] = curr[j - 1] + 1;

        return true;
    }

    size_t generate_impl() noexcept __attribute((noinline))
    {
        size_t count = 0;
		count++;
		print_current();
        while (generate_one()) {
            count++;
            print_current();
        }

        return count;
    }

    void print_current() noexcept 
    {
        if (UNLIKELY(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';
    }
    PythonCombinator comb{k, iota(n), print};
    comb.generate();
    return 0;
}

...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40045216
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
до кучи добавлю питонизацию алгоритма 22272948 ,
перебирает комбинации по 6 из 100 за 84 сек:

Код: python
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.
def gen2(n, k):
  p=list(range(n-k,n))
  Result=0
  i=k
  while 1:
    i-=1
    while i>0:
      n-=1
      p[i]=n
      i-=1
    while 1:
      n-=1
      p[0]=n
      Result+=1
      #print(p)  #обработка очередного сочетания
      if n<=0:
        break
    while 1:
      while 1:
        i+=1 
        if i>=k:
          return Result
        n=p[i]
        if i<n:
          break
      n-=1
      p[i]=n
      if i<n:
        break
      Result+=1
      #print(p)  #обработка очередного сочетания

import time
t1=time.time()
ct=gen2(100,6)
t2=time.time()
print(ct, round(t2-t1,3))
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40045220
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov, спасибо. Насколько я понимаю CPython - это и есть реализация Python3.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40045221
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Aleksandr Sharahov, спасибо. Насколько я понимаю CPython - это и есть реализация Python3.


я тоже так думаю )

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

Код: sql
1.
__init__



например. Зачем так сделано? ХЗ. Язык должен быть эстетически красив и радовать. Причем масса других ключевых
слов - с маленькой буквы а True/False почему-то с большой.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40045237
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Язык должен быть эстетически красив и радовать.
Это точно не про с++. И не про яву.
Подумал сразу, что если в известном поизведении LA Creazione в дающую справа руку вложить мордофон? Веление времени.
Что станет с эстетикой?
И вообще, по-мойму из 3-х свойств ЯП (красота, радость, скорость) осуществимы не более 2-х.
...
Рейтинг: 0 / 0
Генерация сочетаний из N по K
    #40045286
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ruby мог-бы быть в списке красивых языков.
...
Рейтинг: 0 / 0
174 сообщений из 174, показаны все 7 страниц
Форумы / Программирование [игнор отключен] [закрыт для гостей] / Генерация сочетаний из N по K
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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