Страницы

Поиск по вопросам

Показаны сообщения с ярлыком комбинаторика. Показать все сообщения
Показаны сообщения с ярлыком комбинаторика. Показать все сообщения

пятница, 20 марта 2020 г.

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

#python #python_3x #список #комбинаторика


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

test = list(itertools.combinations('0123456789', 9))


Получается список кортежей: 

[('0', '1', '2', '3', '4', '5', '6', '7', '8'), ('0', '1', '2', '3', '4', '5', '6',
'7', '9') ...


Ума не приложу как его можно превратить в такой список: 

['012 345 678', '012 345 679', '012 345 689', ...


Подскажите пожалуйста какой нибудь способ решить эту задачку?
    


Ответы

Ответ 1



import itertools a = list(itertools.combinations('0123456789', 9)) b =[[''.join(y[i:i+3]) for i in range(0, len(y), 3)] for y in a] res = [' '.join(x) for x in b] print(res) ['012 345 678', '012 345 679', '012 345 689', '012 345 789', '012 346 789', '012 356 789', '012 456 789', '013 456 789', '023 456 789', '123 456 789']

воскресенье, 8 марта 2020 г.

Генерация всех подмножеств множества мощности n,которые не содержат соседних элементов

#python #алгоритм #комбинаторика


Предположим у меня есть множество A={1,2,...,n}.(длинна n известна)

Как мне сгенерировать все подмножества,которые не содержат соседних елементов?" 
 (к примеру,если я беру элемент 2 из A, то то элементы 1 и 3 я взять уже не могу)

P.S:Сначала думал просто сгенрировать все подмножества, а потом выбрать те,которые
не содержат соседних элементов.Но такой подход очень не эффективен.

P.P.S.:рекурсией пользоваться нельзя 
    


Ответы

Ответ 1



Стоит начать с того, что максимальная мощность такого подмножества будет ceil(n/2). Минимальная - единица. Соответсвенно, нужно сгенерировать все подмножества длиной 1, затем длиной 2, затем ..., и так далее до ceil(n / 2). Суть данного решения в том, что есть зафиксированные элементы и итератор, который добирает из множества нужное количество элементов. Например для подмножеств мощностью 2: есть подмножества {1,3}, {1,4}, {1,5}, {1,6}. Фиксированный элемент здесь - 1, и итератор собирает все элементы последовательно, начиная с 3. Когда итератор дойдет до конца - фиксированный элемент меняется. Так как подмножества могут быть длиннее, чем 2, то фиксированный элемент - это тоже множество - переменная subset_base. Для каждого нового "уровня" (для подмножества с мощностью + 1) - фиксированные элементы не генерируются заново, а берутся прямиком с предыдущего уровня и затем дополняются. Ключевая строка, пожалуй - это subset_first = tpl[1] + 2 - именно здесь определяется, что сосед не попадет в подмножество. Можно изменять слагаемое 2, определяя критерий "соседства". Необходимо n / 2 раз пройти по всему множеству. Таким образом сложность O(n^2). import math A = {1, 2, 3, 4, 5, 6, 7, 8} subsets = [{elem} for elem in A] A = sorted(list(A)) # Для удобства seeker_position = 0 subset_first = 0 subset_length = 2 base_subsets_for_next_level = [({elem}, index) for index, elem in enumerate(A)] while subset_length <= math.ceil(len(A) / 2): helper = [] for tpl in base_subsets_for_next_level: subset_base = tpl[0] subset_first = tpl[1] + 2 while subset_first < len(A): subset = subset_base | {A[subset_first]} subsets.append(subset ) helper.append((subset, subset_first)) subset_first += 1 base_subsets_for_next_level = helper seeker_position += 1 if seeker_position + 2 * (subset_length - 1) >= len(A): subset_length += 1 seeker_position = 0 subsets.append({}) # Пустое множество print(subsets) Результат: [set([1]), set([2]), set([3]), set([4]), set([5]), set([6]), set([7]), set([8]), set([1, 3]), set([1, 4]), set([1, 5]), set([1, 6]), set([1, 7]), set([8, 1]), set([2, 4]), set([2, 5]), set([2, 6]), set([2, 7]), set([8, 2]), set([3, 5]), set([3, 6]), set([3, 7]), set([8, 3]), set([4, 6]), set([4, 7]), set([8, 4]), set([5, 7]), set([8, 5]), set([8, 6]), set([1, 3, 5]), set([1, 3, 6]), set([1, 3, 7]), set([8, 1, 3]), set([1, 4, 6]), set([1, 4, 7]), set([8, 1, 4]), set([1, 5, 7]), set([8, 1, 5]), set([8, 1, 6]), set([2, 4, 6]), set([2, 4, 7]), set([8, 2, 4]), set([2, 5, 7]), set([8, 2, 5]), set([8, 2, 6]), set([3, 5, 7]), set([8, 3, 5]), set([8, 3, 6]), set([8, 4, 6]), set([1, 3, 5, 7]), set([8, 1, 3, 5]), set([8, 1, 3, 6]), set([8, 1, 4, 6]), set([8, 2, 4, 6])] `

среда, 4 марта 2020 г.

Генерация сочетаний

#алгоритм #математика #комбинаторика


Необходимо построить сочетания всех возможных размеров так, чтобы элементы в сочетаниях
не совпадали (1,2,3; 1,3,2; 2,1,3; 2,3,1; 3,1,2; 3,2,1 - одни и те же сочетания)

Например, даны числа: 1,2,3,4

Ответ:

1
2
3
4

12
13
14
23
24
34

123
124
134

1234

Можно и псевдокод.
    


Ответы

Ответ 1



Вы забыли еще 234 и пустое сочетание :) Берем число, каждому биту которого соответствует один элемент вашего множества, и проходим все числа в цикле - от 00..00 до 11..11. Все. Каждое число - это одно "сочетание" (вообще-то это генерация всех подмножеств данного множества), которое строим, выбирая элементы, соответствующие единичным битам... Например, ваше 1234 - соответственно, 4-битное число... 0000 пустое множество 0001 1 0010 2 0011 12 0100 3 .... 1110 432 1111 4321

Как правильно получить все комбинации чисел в Python

#python #комбинаторика


Есть код:

import itertools

for i in itertools.combinations_with_replacement('0123456789', 4):
    print(''.join(i))


Мне нужно получить все комбинации цифр по 4 с повторениями.
Часть вывода программы:

0000
0001
0002
0003
0004
0005
0006
0007
0008
0009
0011
0012
0013
0014
0015
0016
0017
0018
0019
0022
0023
0024
0025


Скажите, почему после 0009 идет 0011, а не 0010 ?
Также и с 0022.
    


Ответы

Ответ 1



Потому что для вашей задачи вам нужно брать не combinations_with_replacement, а product: import itertools for i in itertools.product('0123456789', repeat=4): print(''.join(i)) Комбинации в комбинаторике не учитывают порядка элементов. Поэтому набор тех же элементов будет считаться одной и той же комбинацией, даже если элементы в другом порядке. Ваш код один раз уже напечатал комбинацию из трёх нулей и одной единицы (0001), поэтому он не будет уже печатать другие варианты этой комбинации - 0010, 0100 и 1000. Аналогично и с другими числами.

пятница, 14 февраля 2020 г.

Перебор всех комбинаций элементов из заданных n множеств

#java #cpp #алгоритм #комбинаторика #множества


Как можно сгенерировать все возможные комбинации, состоящие из элементов заданных
n множеств ? Например, если имеется 3 множества:

A=1,2,3,4,...
B=a,b,c,d,...
C=A,B,C,D,...


и нужно получить что-то вроде:

1 a A; 
1 b A; 
1 c A
....
1 a B; 
1 b B


При этом число n заранее неизвестно, так что вложенными циклами решить не получается.
    


Ответы

Ответ 1



Представьте себе, что это - цифры, стоящие на соответствующих местах числа. Создаем первое число - 1aA (или сколько там нужно мест). Потом просто увеличиваем последний элемент, пока не переберем все. После этого выставляем его равным минимальному элементу и увеличиваем предыдущий (если он тоже максимален - сбрасываем его в минимум и переходим к предыдущему). 1aA 1aB 1aC // достигли максимума в последней позиции. переход- 1bA 1bB 1bC // достигли максимума в последней позиции. переход- 1bA 1bB 1bC // достигли максимума в последней позиции. переход- 1bA 1bB 1bC // достигли максимума в последней позиции. переход- 1сA 1сB 1сC // достигли максимума в последней позиции. переход- но во второй позиции // тоже максимум, перенос далее 2aA ... Словом, просто реализуем алгоритм M (Генерация в смешанной позиционной системе счисления) со страницы 330 4А тома "Искусства программирования"...

Поиск чисел удовлетворяющих условию

#алгоритм #комбинаторика


Необходимо написать программу, которая определяет количество троек натуральных чисел
(x,y,z), таких что:

0 < x < y < z < N, 

Где N - натуральное число, 0 < N < 1000

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


Ответы

Ответ 1



Как уже написали в комментариях, посчитать число троек (x, y, z) натуральных чисел, таких что 0 < x < y < z < N можно так: Сначала уберём условие упорядоченности, то есть посчитаем число троек (x, y, z), таких что каждое из трёх чисел больше нуля и меньше N Получится N - 1 * N - 2 * N - 3 троек (первое число можно выбрать N - 1 способами, второе N - 2 способами, ...) Теперь рассмотрим три различных числа, например {1, 2, 3}. Всего троек, составленных из этих чисел будет 6 = 3 * 2 * 1 штук (следствие предыдущего пункта для N равного 3+1). Нам подходит ровно одна из этих шести троек — когда числа стоят по возрастанию Таким образом, ответ равен (N - 1) * (N - 2) * (N - 3) / 6

суббота, 1 февраля 2020 г.

Алгоритм: все возможные целые из наборов от .. до

#рекурсия #комбинаторика #алгоритм #javascript


Вроде простая задача, но что-то лыжи не едут.

Дан массив диапазонов — пар мин. и макс. значений. Только целые числа.
Нужно найти все возможные целые, получаемые суммированием «допустимых» значений из
произвольного комплекта идущих подряд диапазонов, по одному из каждого участвующего.
Например, дано три диапазона:
[{"min":1,"max":2},{"min":3,"max":4},{"min":5,"max":6}]

Можно получить числа [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12]. 
На деле пар больше, значения веселее. Может, есть известный красивый алгоритм?
В общем смысл моего, далеко не оптимального, решения, такой (перебор): 

распаковать пары min:max до массивов допустимых значений [[1,2],[3,4],[5,6]]
перебирать массивы слева направо;
для каждого варианты глубины вправо от 0 (только себя), до правого края;
перебрать все возможные комбинации значений между этими массивами, по одному зн.
из каждого. Суммировать, уникальные значения сохранить в результат.

Интересно ещё решить обратную задачу: даны опять эти пары, и число. Разложить его
в идущие подряд допустимые значения. 
Усложнённая условием про «подряд» Subset Sum Problem? Подскажите, как называются
алгоритмы для похожих задач?    


Ответы

Ответ 1



Складывая два интервала [a,b]+[c,d] получим итервал [a+c,b+d], если интервалов больше то будет [a1+a2+...+an,b1+b2+...+bn]. Таким образом мы избавляемся от лишних вычислений, в частности от рекурсии совсем. Нам достаточно знать для каждой комбинации интервалов сумму их минимумов и сумму их максимумов. Чтобы перебрать все комбинации интервалов будем двигать границы в списке интервалов. Т.е. сначала будем смотреть первый интервал, потом первый и второй, потом с первого по n-й, затем второй, со второго по третий и т.д. var a = [{"min":2,"max":3},{"min":2,"max":2},{"min":2,"max":2}]; var a_l = a.length; var res = []; for (var i=0;i

Ответ 2



вот такое решение не подойдет ? var ranges=[{"min":2,"max":3},{"min":1,"max":20},{"min":3,"max":5},{min: 2,max: 10}]; //var ranges=[{"min":2,"max":3},{"min":2,"max":2},{"min":2,"max":2}] function range2array(range) { var ret=[]; for (var i=range.min;i<=range.max;i++) ret.push(i); return ret; } function arrays_summ(array1,array2) { var ret=[]; if (array2===undefined) return array1; for (var i in array1) for (var n in array2) ret.push(array1[i]+array2[n]); return ret; } function process(ranges) { var lastarray; var result={}; for (var i in ranges) { lastarray=arrays_summ(range2array(ranges[i]),lastarray); for (var n in lastarray) result[lastarray[n]]=1; } return result; } function keys2array(hash){ var ret=[]; for (var i in hash) { ret.push(i); } return ret.sort(function(a,b) {return (a*1)>(b*1)?1:-1;}) } console.log(keys2array(process(ranges))); http://jsfiddle.net/oceog/mhT5Z/

Ответ 3



Пример здесь http://jsfiddle.net/8cdez/7/ Осталось только обернуть в красивую функцию, но само решение не очень красивое, плюсики ответу не ставьте, пожалуйста. Пусть будет. test=[{"min":2,"max":3},{"min":2,"max":2},{"min":2,"max":2}]; res=new Array(); for (var x=test[0].min; x<=test[0].max; x++) { for (var y=0; y<=test[1].max; y++ ? y!=0: y=test[1].min) { for (var z=0; z<=test[1].max; z++ ? z!=0: z=test[2].min) { res.push(x+y+z); } } }; res.push(test[1].min+test[2].min); res.push(test[1].max+test[2].max); res=_.unique(res.sort()); alert(res); Используется UnderScore (функция unique).

воскресенье, 26 января 2020 г.

Как найти клики / одинаковые комбинации в битовом массиве?

#массивы #алгоритм #математика #комбинаторика


В массив у каждого элемента u1..uN есть своя комбинация битовых свойств b1..bN.

Задача найти наиболее крупные группы с одинаковыми включенными битами. Например:



Зеленым отмечены «включённые» биты, и ниже – две найденные группы. Чем-то задача
похожа на поиск клик в графе, но вроде бы, задача с графом сложнее. Первый найденный
кластер: строки u1 и u2 обе содержат включенными биты 1,3,4,5. Второй кластер: строки
2,3,4 одинаково содержат включенными биты 2 и 4.

В моей задаче число строк измеряется миллионами, а свойств (бит) – сотнями. Поэтому
надеюсь, что есть какое-то более эффективное решение, чем перебор всех возможных комбинаций.

Находить собираюсь наиболее крупные «компании», ограничив поиск неким порогом. Бизнес
цель – анализ данных пользователей в соц. сетях для выявления значимых «общностей».

Подскажите, какая здесь математика поможет?

Связанный вопрос, задавал раньше.
    


Ответы

Ответ 1



Можно поменять строки (а потом - столбцы) так, чтобы включённые биты максимально приблизились к левому углу. Это приводит задачу к каноническому виду, поскольку значительное количество разных массивов сводятся к одному. В размерностях приведённого примера выигрыш по количеству масок в 4!*5! = 2880 раз, в то время как затраты на перестановку столбцов и строк по данным сортировки эквивалентны обработке ~+10 масок P.S. К полученному массиву можно применить алгоритм Кадане 2D, который можно усложнить вычёркиванием одной-двух строк (столбцов), в зависимости от размерности массива.

среда, 22 января 2020 г.

Комбинаторная задача на перестановки

#алгоритм #оптимизация #комбинаторика #динамическое_программирование


Есть 3 числа: A, B и C. Необходимо получить выражение A+B=C, при этом чтоб C было
минимальным. Нужно переставлять биты внутри чисел (переставлять можно только в пределах
одного числа. Т.е можно поменять какие-то 2 бита внутри числа А, но нельзя поменять
два бита из А и B).

Ограничения на числа 2^31-1.

Решения может не существовать, все числа положительные.

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

Подскажите в какую сторону копать.
    


Ответы

Ответ 1



Пишу только алгоритм, автору думаю будет интересно самому реализовать. 1 - Перебираем число С (если делать правильно, то будет не больше 30 млн вариантов, С[31,15] ). В порядке возрастания. Теперь проверим можно ли получить фиксированное С. Для этого используем динамику. Начальное значение F[0][ bit_count(A)] [bit_count(B)][0] = true; Поля: {бит в С, осталось битов в А, осталось битов в Б, есть перенос бита}. Значение - можно ли такое получить. Пересчёт. Если в С бит {i} совпадает с переносом, то переходы либо два нуля или 2 едиинцы {(a-1, b-1), (a,b)} флаг переноса выставить в первом случае. Аналогично если не совпадают { (a-1,b), (a,b-1) } флаг переноса не меняется. Если F[32][0][0][0] = true то мы нашли что хотели. Восстановить ответ можно и не сложно (в крайнем случае ещё раз эту же динамику запустить). Сложность порядка оценить тяжело, оценка сверху 30кк*32*32 но она очень грубая. В целом должно успеть (делать отсечение по отрицательному количеству не забывать).

Насколько эффективен мой метод решения?

#python #python_3x #инспекция_кода #комбинаторика #сложность


И вновь всех приветствую. Решил очередную задачу на контесте способом, который показался
мне интересным. Т.к. я новичок, еще не совсем понимаю как определять О(n), поэтому
прошу помощи в этом вопросе.

Собственно, вот сама задача:


  На вход программы поступает последовательность из N целых
  положительных чисел. Рассматриваются все пары различных элементов
  последовательности  (элементы пары не обязаны стоять в
  последовательности рядом, порядок элементов в паре неважен). 
  Необходимо определить количество пар, сумма чисел и разность индексов
  которых кратна 3.


И мое решение:

n = int(input())
summ = 0
r = [[0] * 3 for i in range(3)]
for i in range(n):
    s = int(input())
    r[i % 3][s % 3] += 1
print((r[0][0] * (r[0][0] - 1) + r[1][0] * (r[1][0] - 1) + r[2][0] * (r[2][0] - 1))//2
+ r[0][1] * r[0][2] + r[1][1] * r[1][2] + r[2][1] * r[2][2])


Проверку на контесте прошла, результат - задача решена верно.
    


Ответы

Ответ 1



Решение отличное, сложность линейная от размера ввода В цикле выполняется k*n операций, где k - константа, так что его сложность O(n) Начальные строчки выполняются за константное время O(1), финальная тоже, итого O(n + 1 + 1) = O(n)

четверг, 9 января 2020 г.

Число комбинаций при которых хотя бы одна цифра на своем месте

#математика #комбинаторика


Есть 5 позиций (первая-пятая). Есть 5 цифр 1-5. Как посчитать число комбинаций, при
которых хотя бы одна цифра будет на своей позиции (1 в первой или 5 в пятой, или 12345
и тд). Для частного случая посчитать могу, а формулу в общем виде вывести не получается.
    


Ответы

Ответ 1



Все перестановки = N! Все беспорядки(!n) = Сумма[k=0 -> N]((-1)^k N!/k!) Перестановки, содержащие хотя бы одну неподвижную точку = N! - !n Для N = 5: !n = N! - N!/1 + N!/2! - N!/3! + N!/4! - N!/5! = N!(1/2 - 1/6 + 1/24 - 1/120) N! - !n = N!(1 - 1/2 + 1/6 - 1/24 + 1/120) = 120(...) = 120 - 60 + 20 - 5 + 1 = 76 Беспорядки

пятница, 13 декабря 2019 г.

Как вычислить все возможные комбинации элементов в массиве?

#javascript #алгоритм #разработка_игр #комбинаторика


Пишу игру на js. Есть массив из 5 элементов^
    ['a', 'b', 'c', 'd', 'e'];

Мне нужно получить все возможные сочетания элементов этого массива при том, что элементов
может быть от 1 до 5. Т.е.:
    ['a', 'b', 'c', 'd']; ['b', 'e']; ['a', 'c', 'd']; ['d'] итд...

Как это сделать?
    


Ответы

Ответ 1



То, что вы хотите, называется не сочетаниями, а кортежами. Простейший способ - цикл от 0 до 31 (2^5-1), и в каждом кортеже имеются те элементы, которые соответствуют единичным битам числа. Другие варианты генерации всех кортежей вы можете найти в книге Д. Кнута Искусство программирования, т.4А.

Ответ 2



JS не очень хорошо знаю, но попробую предложить решение. В цикле перебираете числа от 1 до 31. Для каждого значения переводите в двоичную форму. Например так: i.toString(2) Получаете строку примерно такого вида: 11010 Выбираете те элементы массива, для которых на соответствующей позиции стоит 1, и игнорируете те, для которых ноль. Например с помощью функции filter: var arr = ['a', 'b', 'c', 'd', 'e'] for(i=1; i<=31; i++) { b = i.toString(2) while(b.length < 5) {b = '0' + b} var farr = arr.filter( function(item, n, arr) { return b.charAt(4-n) > 0 }) document.writeln(farr + '
') }

среда, 11 декабря 2019 г.

Как найти все матрицы состоящие из заданного числа элементов, с ненулевым определителем?

#математика #комбинаторика #матрицы


Имеется матрица 7×7. Элементами матрицы может быть любой из 33 элементов. Если мы
перебором всевозможные матрицы, то количество комбинаций будет равно  33^49. Это очень
долго для компьютера. Но мне не нужны все матрицы, мне нужны лишь те, которые имеет
ненулевой определитель. Какие свойства матриц можно использовать, чтобы ускорить поиск
неворожденных матриц? Ведь количество таких матриц ограничено в сравнении с общим количеством
комбинаций. Какие будет предложения?
    


Ответы

Ответ 1



В постановке ОП задача нерешаема. Если 33 ненулевых элемента не равны попарно, то существует 33^7 = 42618442977 вариантов их размещения на главной диагонали, . Если это не смущает, можно разместить те же элементы в любой из 7! = 5040 "ладейных" расстановок (по одному на строку и по одному на столбец). И такие коэффициенты можно добавлять многократно (например, использовать свойства блочных матриц). UPD. Если задать элементы ниже главной диагонали нулевыми, элементы главной диагонали - ненулевыми, а элементы выше главной диагонали - произвольными, то определитель будет ненулевым (и равным произведению элементов главной диагонали). Если нуль использовать нельзя, то можно заполнить матрицу ниже главной диагонали одним и тем же элементом, главную диагональ - неравными ему элементами, а матрицу выше главной диагонали - произвольно. В этом случае определитель будет произведением разностей элементов главной диагонали и "нижнего" элемента. Перестановка строк и столбцов матрицы может изменить только знак её определителя. Таким образом, количество ненулевых определителей не меньше, чем 33 * 327 * 3321 * 7! = 5040 * 3322 * 327 ≈ 4 * 1047

вторник, 10 декабря 2019 г.

Задача про паркет

#алгоритм #комбинаторика #динамическое_программирование


Есть поле из клеток, размеры которого [n * m]. Необходимо покрыть это поле плитками
размера [1 * 2] (их можно поворачивать) таким образом, чтоб все клетки поля были накрыты,
и чтоб плитки не вылазили за границы поля. Задача состоит в том, чтоб найти количество
таких покрытий.

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

Затем я считаю матрицу ответов:

if (dp[j][i]) ans[k][i] += ans[k - 1][j];


И после посчитанной матрицы ответов считаю ответ на задачу

if (isFinishing(i)) res += ans[m - 1][i];

Эта строчка означает, что если профиль может являться завершающим, то добавить это
число в ответу.

Данное решение вполне себе работает. Но затем я нашел модификацию данной задачи.
Модификация состоит в том, чтобы найти количество таких покрытий, в которых не будет
сплошных линий из плиток [1 * 2].

Пример: 

Первая картинка - это пример того, какими должны быть покрытия, вторая - какими не
должны. 

У меня был вариант хранить в динамике ещё состояние для каждой строки, была ли она
перекрыта хоть раз вертикальной плиткой, и добавлять к ответу только эти варианты.
Возможно, это бы и работало, но я не особо понимаю, как это реализовать. 

Подскажите, как реализовать, или предложите свои варианты решения.
    


Ответы

Ответ 1



Можно сделать динамически по рядам длины n. Для каждого промежуточного решения хранить состояние "дырок", куда можно подставить вертикальные плитки. Установим, что мы всегда движемся сверху вниз, значит дырки будут внизу. Динамический массив будет иметь вид: [int ряды, bool[] дырки] = кол-во решений Например, все возможные решения для 1 ряда длиной 4: [1, [0, 0, 1, 1]] = 1 // [xx] . . [1, [1, 0, 0, 1]] = 1 // . [xx] . [1, [1, 1, 0, 0]] = 1 // . . [xx] [1, [1, 1, 1, 1]] = 1 // . . . . 0 - горизонтальная плитка, 1 - пустое место для будущей вертикальной плитки. Количество нулей идущих подряд всегда должно быть парным, все нули запрещены (что бы удовлетворить ваше условие). Дальше мы пытаемся соединить решение для x-1 рядов с решением для 1 ряда, что бы получить решение для x рядов: for (prev in solutions[x-1]) for (single in solutions[1]) { if (дырки prev совместимы c дырками single) { holes = считаем как будут выглядеть дырки после совмещения 2 решений solutions[x][holes] += prev; } } Конечным решением будет значение solutions[m, [0, 0, 0, 0, ... 0]], где все нули - это полностью заполненный последний ряд. Возможно существует более элегантное решение.

воскресенье, 8 декабря 2019 г.

Массив всех возможных комбинаций из '0' и '1'

#c_sharp #алгоритм #комбинаторика


Нужно создать двумерный битовый (булевый) массив всех возможных комбинаций из 0 и
1 (false и true), размерность массива - параметр. Например, для входящего значения
3 результат должен быть

0   0   0,
0   0   1,
0   1   1,
1   0   0,
1   0   1,
1   1   1


Что-то завис. 
    


Ответы

Ответ 1



Если 010 и 110 пропущены случайно, то результат - это двоичные представления всех чисел от 0 до 2n-1: int size = 3; bool[][] result = Enumerable.Range(0, 1 << size) .Select(i => new BitArray(new int[] { i }).Cast().Take(size).ToArray()) .ToArray();

Ответ 2



Если интересуют именно все возможные комбинации, и нет острой необходимости иметь результат в виде массива, то можно создать класс, реализующий индексное свойство: public sealed class BitArray2D { private readonly int highestBitIndex; public BitArray2D(int bitsCount) { highestBitIndex = bitsCount - 1; } public bool this[int rowIndex, int colIndex] { get { return (rowIndex & (1 << (highestBitIndex - colIndex))) != 0; } } } Этот класс позволит не выделять лишнюю память, а будет просто быстро вычислять значения при обращении. Пример: var t = new BitArray2D(3); Console.WriteLine(t[3, 0]); // False Console.WriteLine(t[3, 1]); // True Console.WriteLine(t[3, 2]); // True

Ответ 3



Для языка PHP: $arr = range(0, 1<<$k-1);

среда, 27 ноября 2019 г.

Все возможные комбинации одномерного массива

#комбинаторика


Друзья подскажите в теории, какие бывают практики к подходу вычисления количества
возможных комбинаций из элементов одномерного массива. И перебрать все возможные последовательности.
Например есть элементы 2,45,16,34 - как узнать возможные комбинации? ( не прошу кода,
прошу теорию)    


Ответы

Ответ 1



Перестановки Перестановки - это комбинации изначального массива, получаемые перестановкой элементов. Количество перестановок An = n! Алгоритм получения перестановки по номеру (1..n!) таков: var facts = []; function fact(N){ if(N==0 || N==1) return 1; if(facts[N]) return facts[N]; facts[N] = N*fact(N-1); return facts[N]; } function permutation(index, A){ var n=A.length; var i=index+1; var res=[]; for(var t=1;t<=n;t++){ var f = fact(n-t); var k=Math.floor((i+f-1)/f); res.push(A.splice(k-1,1)[0]); i-=(k-1)*f; } if (A.length) res.push(A[0]); return res; } function log(){ var msg = Array.prototype.slice.call(arguments).join(" "); document.getElementById("log").value+="\n"+msg; console.log(arguments); } var M = ["A","B","C","D","E"]; for(var i=0;i Смысл заключается в том, что мы на каждой итерации берем элемент из массива и убираем его, переходя к следующей итерации, имеем массив меньшей длины... и так продолжаем пока исходный массив не опустеет. При этом номер комбинации получается исходя из порядка вынимания элементов массива. Аналогичный подход используется в алгоритме Фишера–Йетса для перемешивания массива, только там элемент, который будет выбран на каждой итерации берется случайным образом. Сочетания Сочетания - это наборы определенной длины (k), составленные из множества определенной длины (n). Сочетания, в которых одни те же элементы поменены местами, считаются одним сочетанием, поэтому для удобства берутся те сочетания, элементы в которых упорядочены по возрастанию (в лексикографическом порядке). Количество сочетаний C(n,k) - читается как "Це из эн по ка", = n!/(k!(n-k)!), называются биномиальными коэффициентами. Алгоритм получения сочетания по номеру таков: var facts = []; function fact(N) { if (N == 0 || N == 1) return 1; if (facts[N]) return facts[N]; facts[N] = N * fact(N - 1); return facts[N]; } function C(n, k) { return fact(n) / fact(k) / fact(n - k); } function combination(index, k, A) { var res = [0]; var n = A.length; var s = 0; for (var t = 1; t <= k; t++) { var j = res[t - 1] + 1; while ((j < (n - k + t)) && ((s + C(n - j, k - t)) <= index)) { s += C(n - j, k - t); j++; } res.push(j); } res.splice(0, 1); return res; } function log() { var msg = Array.prototype.slice.call(arguments).join(" "); document.getElementById("log").value += "\n" + msg; console.log(arguments); } var M = ["A", "B", "C", "D", "E"]; for (var i = 0; i < C(M.length, 3); i++) { log(i, combination(i, 3, M.slice(0)).join("")); } html, body { margin: 0; padding: 0; height: 100% } #log { width: 100%; height: 100%; border: 0; padding: 0; display: block } Номер сочетания берется как сумма всех биномиальных коэффициентов для массивов уменьшающейся длины (на самом деле сформулировать кратко и притом понятно принцип нумерации сложно, ссылка на теорию будет ниже). Размещения Сочетания и перестановки являются частными случаями размещений. Размещения - это сочетания, где важен порядок элементов. Или, другими словами, это перестановки сочетаний. Количество размещений A(n,k)=k!*C(n,k)=n!/(n-k)!. Таким образом, чтоб получить размещение по номеру, делим общее количество размещений на цело на номер - получаем номер сочетания, и применяем к нему перестановку с номером как остаток от деления количества размещений на номер размещения. Размещения с повторениями Отдельным вариантом комбинации является размещение с повторением. A'(n,k) = n^k. Т.е. все варианты массивов длины k, где на каждой позиции может быть любой элемент из множества размера n. Самый простой для понимания вариант - это A(10,k) - все десятичные числа от 0 до 10^k-1. Или A(2,k) - все двоичные числа длины k. Нумерация элементов натуральная, индекс комбинации соответствует десятичному аналогу числа в n-ричной системе счисления. См. также Про нумерацию размещений и сочетаний можно почитать в статье "О нумерации перестановок и сочетаний для организации параллельных вычислений в задачах проектирования управляющих систем" (гуглится), алгоритмы приведены оттуда, ссылки в статье ведут на: Дейкстра Э. Дисциплина программирования. Липский В. Комбинаторика для программистов. Оптимизация Поскольку расчеты ведутся с использованием факториалов, то для больших значений n,k скорее всего может потребоваться длинная арифметика. В то же время вполне возможно, что точное вычисление факториала не понадобится (надо проверять), и достаточно будет формулы Стирлинга... В приведенных алгоритмах функция факториала написана для простоты понимания. Обратная задача Каждый из вариантов комбинаций может иметь обратную задачу - получение номера по комбинации. Имея представление о принципе нумерации обратная задача также решается. Например, для размещений с повторениями - это перевод n-ричной системы счисления в десятичную... Использование Имея рассчитанные значения факториалов или вообще таблиц со всеми комбинациями определенного типа есть возможность получения случайной комбинации с использованием только одного вызова ГСЧ для получения комбинации.

Ответ 2



Не знаю, как это делается на php, но в теории можно применить следующий алгоритм: Перебираем все числа от 0 (ни одного элемента) до (2^n) - 1 (все элементы), где n - длина масcива. На каждом шаге перебора смотрим все биты числа, и если i-й бит равен единице, то i-й элемент будет входить в комбинацию. Например, для трёх элементов: 0 - нет элементов 1 - (001) только 1й элемент 2 - (010) только 2й 3 - (011) 2й и 1й 4 - (100) только 3й 5 - (101) 1й и 3й 6 - (110) 1й и 2й 7 - (111) 1й, 2й, 3й

Ответ 3



var facts = []; function fact(N){ if(N==0 || N==1) return 1; if(facts[N]) return facts[N]; facts[N] = N*fact(N-1); return facts[N]; } function permutation(index, A){ var n=A.length; var i=index+1; var res=[]; for(var t=1;t<=n;t++){ var f = fact(n-t); var k=Math.floor((i+f-1)/f); res.push(A.splice(k-1,1)[0]); i-=(k-1)*f; } if (A.length) res.push(A[0]); return res; } function log(){ var msg = Array.prototype.slice.call(arguments).join(" "); document.getElementById("log").value+="\n"+msg; console.log(arguments); } var M = ["п","о","б","а","е","с","п","р","о","с","и","у"]; for(var i=0;i

четверг, 11 июля 2019 г.

Как составить алгоритм для поиска комбинаций в массивах?

Здравствуйте, помогите пожалуйста придумать алгоритм для поиска всех возможных групп из комбинаций. Исходные данные: Массив комбинаций $combinations => [ a => [11, 38], b => [38, 64, 71], c => [11, 24, 38, 65] d => [128, 38, 57, 40] ... ] каждая комбинация в этом массиве может состоять из 2, 3 или 4 чисел Нужно найти все возможные группы из 4х чисел, в которых будут участвовать от 2-х до 4-х комбинаций из массива комбинаций и определить сколько комбинаций входит в группу и каких. В группе должно быть 4 числа, ни больше, ни меньше. Например можно составить группу в которой будут участвовать 2 комбинации (a и b): $group => [11, 38, 64, 71] В эту группу входит первая и вторая комбинация. Третья комбинация (с) уже содержит в себе первую комбинацию (a), т.е. она тоже состоит из двух комбинаций. Как найти группы, в которых будет от 2х до 4х комбинаций? Я нагородил много вложенных циклов и то, работает криво и ищет только среди комбинаций которые состоят из 2х чисел. Код получился огромный и страшный, что даже боюсь показывать его кому-то. А страшный он из-за того, что не могу придумать оптимальный алгоритм поиска групп.


Ответ

Код: # Входящий массив: $ar = array( 'a' => '11, 38', 'b' => '38, 64, 71', 'c' => '11, 24, 38, 65', 'd' => '128, 38, 57, 40' );
# Искомые числа: $in = array('11', '38', '64', '71');
$result = array();
foreach($ar as $key => $value){ $line = explode(',',$value);
foreach($in as $inn){ if(in_array($inn,$line)){ $result[$key]['count']++; $result[$key][] = $inn; } } } print_r($result); Результат работы кода: Array ( [a] => Array ( [count] => 2 [0] => 11 [1] => 38 )
[b] => Array ( [count] => 3 [0] => 38 [1] => 64 [2] => 71 )
[c] => Array ( [count] => 2 [0] => 11 [1] => 38 )
[d] => Array ( [count] => 1 [0] => 38 )
) Надеюсь, что помог. UPD: $ar = array( 'a' => '11,38', 'b' => '38,64,71', 'c' => '11,24,38,65', 'd' => '128,38,57,40', 'e' => '38,115,64', 'f' => '64,11,57,38' ); $line = array(); foreach($ar as $key => $value){ $line[$key] = explode(',',$value); } $r = 1; foreach($line as $keyRes => $res){ $c = 1; foreach($line as $keyRes2 => $res2){ if($c > $r){ $arMerge[$keyRes][$keyRes2] = array_merge($res,$res2); }; $c++; } $r++; } foreach($arMerge as $intKey1 => $intVal1){ foreach($intVal1 as $intKey2 => $intVal2){ foreach($intVal2 as $intVal){ $resAr[$intKey1][$intKey2][] = (int)$intVal; } } } foreach($resAr as $uniqKey1 => $uniqVal1){ foreach($uniqVal1 as $uniqKey2 => $uniqVal2){ if(count(array_unique($uniqVal2)) == 4){ $result[] = $uniqKey1.'-'.$uniqKey2; } } } print_r($result); Результат выполнения: Array ( [0] => a-b [1] => a-c [2] => a-e [3] => a-f [4] => b-e ) Теперь вроде правильно.

понедельник, 8 июля 2019 г.

Комбинаторная задача на перестановки

Есть 3 числа: A, B и C. Необходимо получить выражение A+B=C, при этом чтоб C было минимальным. Нужно переставлять биты внутри чисел (переставлять можно только в пределах одного числа. Т.е можно поменять какие-то 2 бита внутри числа А, но нельзя поменять два бита из А и B).
Ограничения на числа 2^31-1.
Решения может не существовать, все числа положительные.
Ну перебором тут не выйдет. Пробовал генетический алгоритм, в некоторых случаях он работает, но, к сожалению, не во всех. Возможно задача решается способами динамического программирования или программированием по профилю, но не особо понимаю, как должна выглядеть динамика.
Подскажите в какую сторону копать.


Ответ

Пишу только алгоритм, автору думаю будет интересно самому реализовать.
1 - Перебираем число С (если делать правильно, то будет не больше 30 млн вариантов, С[31,15] ). В порядке возрастания.
Теперь проверим можно ли получить фиксированное С.
Для этого используем динамику.
Начальное значение F[0][ bit_count(A)] [bit_count(B)][0] = true
Поля: {бит в С, осталось битов в А, осталось битов в Б, есть перенос бита}.
Значение - можно ли такое получить.
Пересчёт. Если в С бит {i} совпадает с переносом, то переходы либо два нуля или 2 едиинцы {(a-1, b-1), (a,b)} флаг переноса выставить в первом случае. Аналогично если не совпадают { (a-1,b), (a,b-1) } флаг переноса не меняется.
Если F[32][0][0][0] = true то мы нашли что хотели. Восстановить ответ можно и не сложно (в крайнем случае ещё раз эту же динамику запустить).
Сложность порядка оценить тяжело, оценка сверху 30кк*32*32 но она очень грубая. В целом должно успеть (делать отсечение по отрицательному количеству не забывать).

четверг, 30 мая 2019 г.

Генерация всех подмножеств множества мощности n,которые не содержат соседних элементов

Предположим у меня есть множество A={1,2,...,n}.(длинна n известна)
Как мне сгенерировать все подмножества,которые не содержат соседних елементов?" (к примеру,если я беру элемент 2 из A, то то элементы 1 и 3 я взять уже не могу)
P.S:Сначала думал просто сгенрировать все подмножества, а потом выбрать те,которые не содержат соседних элементов.Но такой подход очень не эффективен.
P.P.S.:рекурсией пользоваться нельзя


Ответ

Стоит начать с того, что максимальная мощность такого подмножества будет ceil(n/2). Минимальная - единица. Соответсвенно, нужно сгенерировать все подмножества длиной 1, затем длиной 2, затем ..., и так далее до ceil(n / 2). Суть данного решения в том, что есть зафиксированные элементы и итератор, который добирает из множества нужное количество элементов. Например для подмножеств мощностью 2: есть подмножества {1,3}, {1,4}, {1,5}, {1,6}. Фиксированный элемент здесь - 1, и итератор собирает все элементы последовательно, начиная с 3. Когда итератор дойдет до конца - фиксированный элемент меняется. Так как подмножества могут быть длиннее, чем 2, то фиксированный элемент - это тоже множество - переменная subset_base. Для каждого нового "уровня" (для подмножества с мощностью + 1) - фиксированные элементы не генерируются заново, а берутся прямиком с предыдущего уровня и затем дополняются. Ключевая строка, пожалуй - это subset_first = tpl[1] + 2 - именно здесь определяется, что сосед не попадет в подмножество. Можно изменять слагаемое 2, определяя критерий "соседства".
Необходимо n / 2 раз пройти по всему множеству. Таким образом сложность O(n^2).
import math
A = {1, 2, 3, 4, 5, 6, 7, 8}
subsets = [{elem} for elem in A] A = sorted(list(A)) # Для удобства seeker_position = 0 subset_first = 0
subset_length = 2
base_subsets_for_next_level = [({elem}, index) for index, elem in enumerate(A)]
while subset_length <= math.ceil(len(A) / 2): helper = [] for tpl in base_subsets_for_next_level: subset_base = tpl[0] subset_first = tpl[1] + 2 while subset_first < len(A): subset = subset_base | {A[subset_first]} subsets.append(subset ) helper.append((subset, subset_first)) subset_first += 1 base_subsets_for_next_level = helper seeker_position += 1 if seeker_position + 2 * (subset_length - 1) >= len(A): subset_length += 1 seeker_position = 0
subsets.append({}) # Пустое множество print(subsets)
Результат: [set([1]), set([2]), set([3]), set([4]), set([5]), set([6]), set([7]), set([8]), set([1, 3]), set([1, 4]), set([1, 5]), set([1, 6]), set([1, 7]), set([8, 1]), set([2, 4]), set([2, 5]), set([2, 6]), set([2, 7]), set([8, 2]), set([3, 5]), set([3, 6]), set([3, 7]), set([8, 3]), set([4, 6]), set([4, 7]), set([8, 4]), set([5, 7]), set([8, 5]), set([8, 6]), set([1, 3, 5]), set([1, 3, 6]), set([1, 3, 7]), set([8, 1, 3]), set([1, 4, 6]), set([1, 4, 7]), set([8, 1, 4]), set([1, 5, 7]), set([8, 1, 5]), set([8, 1, 6]), set([2, 4, 6]), set([2, 4, 7]), set([8, 2, 4]), set([2, 5, 7]), set([8, 2, 5]), set([8, 2, 6]), set([3, 5, 7]), set([8, 3, 5]), set([8, 3, 6]), set([8, 4, 6]), set([1, 3, 5, 7]), set([8, 1, 3, 5]), set([8, 1, 3, 6]), set([8, 1, 4, 6]), set([8, 2, 4, 6])]
`

вторник, 21 мая 2019 г.

Генерация сочетаний

Необходимо построить сочетания всех возможных размеров так, чтобы элементы в сочетаниях не совпадали (1,2,3; 1,3,2; 2,1,3; 2,3,1; 3,1,2; 3,2,1 - одни и те же сочетания)
Например, даны числа: 1,2,3,4
Ответ:
1 2 3 4
12 13 14 23 24 34
123 124 134
1234
Можно и псевдокод.


Ответ

Вы забыли еще 234 и пустое сочетание :)
Берем число, каждому биту которого соответствует один элемент вашего множества, и проходим все числа в цикле - от 00..00 до 11..11. Все. Каждое число - это одно "сочетание" (вообще-то это генерация всех подмножеств данного множества), которое строим, выбирая элементы, соответствующие единичным битам...
Например, ваше 1234 - соответственно, 4-битное число...
0000 пустое множество 0001 1 0010 2 0011 12 0100 3 .... 1110 432 1111 4321