Комбінаторні вироби унікальних прайметів


21

Постановка проблеми

Давши набір унікальних послідовних прайменів (не обов'язково включаючи 2), генеруйте добутки всіх комбінацій перших потужностей цих праймерів - наприклад, без повторів - а також 1. Наприклад, задавши множину {2, 3, 5, 7}, ви створюєте {1, 2, 3, 5, 6, 7, 10, 14, 15, 21, 30, 35, 42, 70, 105, 210}, оскільки:

  1  =  1
  2  =  2
  3  =  3
  5  =  5
  6  =  2 x 3
  7  =  7
 10  =  2 x 5
 14  =  2 x 7
 15  =  3 x 5
 21  =  3 x 7
 30  =  2 x 3 x 5
 35  =  5 x 7
 42  =  2 x 3 x 7
 70  =  2 x 5 x 7
105  =  3 x 5 x 7
210  =  2 x 3 x 5 x 7

Зауважте, що якщо кардинальність вашого вхідного набору дорівнює k, це дасть вам 2 ^ k членів у вашому вихідному наборі.

Правила / Умови

  1. Ви можете використовувати будь-яку мову. Намагайтеся на найменший кількість символів вихідного коду.
  2. Ваше рішення повинно бути або повною програмою, або повноцінною функцією. Функція може бути анонімною (якщо ваша мова підтримує анонімні функції).
  3. У вашому рішенні має бути підтримка продуктів принаймні до 2 ^ 31. Не турбуйтеся про виявлення чи обробку цілого переповнення, якщо вам передаються числа, продукт яких занадто великий, щоб представляти. Однак, будь ласка, вкажіть межі своїх розрахунків.
  4. Ви можете прийняти або список, або набір, і створити або список, або набір. Ви можете припустити, що вхід відсортований, але вам не потрібно виробляти відсортований вихід.

Фон

Коли або чому це корисно? Одне місце, яке дуже корисно, - це генерувати таблицю множників для гонки паралельно за цілим алгоритмом факторингу, відомим як факторизація квадратних форм. Там кожен непарний множник, який ви спробуєте, зменшує ймовірність виходу з ладу алгоритму (щоб знайти коефіцієнт) приблизно на 50% у жорстких напівпримірах. Так із набором генеруючих простих чисел {3, 5, 7, 11}, який створює набір з 16 пробних множників, щоб переходити паралельно, алгоритм виходить приблизно з 2 ^ -16 часу на жорстких напівпримірах. Додавання 13 до списку простих чисел створює набір з 32 пробних множників, зменшуючи ймовірність виходу з ладу приблизно до 2 ^ -32, що дозволяє різко покращити результат без додаткових обчислювальних витрат (тому що навіть удвічі більше множників, що паралельно гонять, на в середньому він все ще знаходить відповідь у однаковій загальній кількості кроків).

Відповіді:


18

Чистий баш, 32 байти

eval echo \$[{1,${1// /\}*{1,}}]

Список введення даних (розділений пробіл), переданий як аргумент командного рядка.

Використовуються три різних розширення оболонок:

  1. ${1// /\}*{1,}є розширення параметр , який замінює прогалини 2 3 5 7з , }*{1,щоб дати 2}*{1,3}*{1,5}*{1,7. \$[{1,і }]додаються відповідно до початку та кінця, щоб дати \$[{1,2}*{1,3}*{1,5}*{1,7}]. Зворотний \$[косий рядок запобігає спробам зробити арифметичне розширення на цьому етапі.
  2. \$[{1,2}*{1,3}*{1,5}*{1,7}]- це розширення дужок . Оскільки розширення брекетів зазвичай відбувається перед розширенням параметра , ми використовуємо мусимо використовувати, evalщоб примусити розширення параметра спочатку відбутися. Результатом розширення брекетів є $[1*1*1*1] $[1*1*1*7] $[1*1*5*1] ... $[2*3*5*7].
  3. $[1*1*1*1] $[1*1*1*7] $[1*1*5*1] ... $[2*3*5*7]- це список арифметичних розширень , які потім оцінюються, щоб дати список потрібних нам чисел.

Вихід:

$ ./comboprime.sh "2 3 5 7"
1 7 5 35 3 21 15 105 2 14 10 70 6 42 30 210
$

3
Розум ... подув ... ух!
Тодд Леман

Wtf ... я отримую1 0
username.ak

@ username.ak Який ваш внесок? Як ви вводите його (аргументи командного рядка?). Яку версію bash ти працюєш? bash --version
Цифрова травма

12

CJam, 13 байт

1aq~{1$f*+}/p

Читає масив (наприклад, [2 3 5 7]) з STDIN. Спробуйте в Інтернеті.

Анонімна функція матиме те саме число байтів:

{1a\{1$f*+}/}

Приклад виконання

$ cjam <(echo '1aq~{1$f*+}/p') <<< '[]'
[1]
$ cjam <(echo '1aq~{1$f*+}/p') <<< '[2 3 5 7]'
[1 2 3 6 5 10 15 30 7 14 21 42 35 70 105 210]

Як це працює

1a               " Push R := [1].              ";
  q~             " Read an array A from STDIN. ";
    {     }/     " For each a ∊ A:             ";
     1$f*+       "     R += { ra : r ∊ R }     ";
            p    " Print.                      ";

4
Нічого собі, це розумний спосіб перебрати всі підмножини.
Мартін Ендер

9

Хаскелл, 22

рішення - анонімна функція:

map product.mapM(:[1])

Приклад використання:

*Main> map product.mapM(:[1]) $ [2,3,5]
[30,6,10,2,15,3,5,1]

пояснення:
(:[1]) це функція, яка надає число xповертає список [x,1].
mapM(:[1])це функція, яка надає перелік чисел, відображає функцію (:[1])над ними і повертає всі можливі способи вибору елемента з кожного списку. наприклад, mapM(:[1]) $ [3,4]спочатку відображає функцію, яку потрібно отримати [[3,1] , [4,1]]. то можливі варіанти [3,4](вибираючи перше число обох), [3,1] [1,4]і [1,1]тому він повертається [[3,4],[3,1],[1,4],[1,1]].

потім map productвідображає всі варіанти і повертає свої продукти, які є потрібним результатом.

ця функція є поліморфною за своїм типом, тобто вона може працювати на всіх типах чисел. Ви можете ввести його список, Intі результат буде списком, Intале також можна застосувати до списку типуIntegerі повернути список Integer. це означає, що поведінка переповнення задається не цією функцією, а типом вводу (так, виразна система типу Haskell :))


Приємно! Чи є обмеження щодо розміру числа?
Тодд Леман

1
@ToddLehman nope. Типовим числовим типом є Integer, що є необмеженим цілим числом. Там також Intє 32-бітове ціле число, але це в основному лише спадщина.
Джон Дворак

@JanDvorak на практиці так, але я дуже люблю систему типів, щоб не згадувати про неї :). Ще одне, що слід помітити, це те, що це анонімне значення, як ви його використовуєте, оскільки обмеження мономорфізму може застосовуватися в деяких випадках.
гордий haskeller

8

Математика, 18 17 байт

1##&@@@Subsets@#&

Це анонімна функція. Називай це так

1##&@@@Subsets@#&[{2,3,5,7}]

А Мартін налітає на чудово коротку відповідь!
Тодд Леман

@ToddLehman Тепер давайте чекатимемо відповіді J, яка перемагає цю. ;)
Мартін Ендер

1
Якщо Mathematica не була закритим джерелом, хтось може написати версію для гольфу. ×@@@𝒫@#має бути неперевершеним.
Денніс

@Dennis Специфікація мови Wolfram доступна незалежно від Mathematica, і я думаю, що існує одна або дві (неповні) реалізації з відкритим кодом. Створення Unicode-псевдонімованої версії Mathematica було запропоновано кілька разів, але я не думаю, що це було б дуже добре сприйнято в PPCG. ^^
Мартін Ендер

2
@ MartinBüttner Вибачення за те, що ви чекаєте: (*/@#~2#:@i.@^#)16 символів у J;)
алгоритми

4

Оновлення: C (функція f), 92

Навіть як функція, це все ще найдовший запис тут. Це перший раз, коли я передав масив невідомої довжини як аргумент функції в C, і, мабуть, немає ніякої можливості для функції C дізнатись довжину переданого до нього масиву, оскільки аргумент передається як покажчик ( незалежно від використовуваного синтаксису). Отже, необхідний другий аргумент для вказівки довжини.

Я зберігав вихід у stdout, тому що встановлення цілого масиву та повернення його майже напевно буде довше.

Дякуємо Деннісу за поради.

Дивіться функцію f(92 символи, виключаючи зайву пробіл) у тестових програмах нижче.

Вихід через printf

j;

f(int c,int*x){
  int p=1,i;
  for(i=c<<c;i--;p=i%c?p:!!printf("%d ",p))p*=(i/c>>i%c)&1?1:x[i%c];
}

main(int d,char**v){
  d--;
  int y[d];
  for(j=d;j--;)y[j]=atoi(v[j+1]);
  f(d,y);
}

Виводиться через вказівник масиву

j,q[512];

f(int c,int*x,int*p){
    for(int i=-1;++i-(c<<c);p[i/c]*=(i/c>>i%c)&1?1:x[i%c])i%c||(p[i/c]=1);
}

main(int d,char**v){
  d--;
  int y[d];
  for(j=d;j--;)y[j]=atoi(v[j+1]);
  f(d,y,q);
  for(j=1<<d;j--;)printf("%d ",q[j]);
}

C (програма), 108

виключаючи зайвий пробіл.

p=1,i;
main(int c,char**v){
  c-=1;
  for(i=c<<c;i--;i%c||(printf("%d ",p),p=1))(i/c>>i%c)&1||(p*=atoi(v[i%c+1]));
}

Введення з командної лінії, вихід у stdout. C не збирається перемагати тут, але, можливо, я спробую перейти до функції завтра.

В основному ми повторюємо всі 1<<cкомбінації праймерів, причому кожен біт i/cасоціюється з наявністю або відсутністю певного грунту в продукті. "Внутрішня петля" i%cпроходить через праймери, множачи їх відповідно до значенняi/c. Коли i%cдосягає 0, продукт виводиться, а потім встановлюється на 1 для наступної "зовнішньої" ітерації.

Що цікаво, printf("%d ",p,p=1)не працює (він завжди друкує 1.) Це не перший раз, коли я бачив дивну поведінку, коли значення використовується в a printfі призначається пізніше в тому ж дужці. У цьому випадку можливо, що друга кома розглядається не як роздільник аргументів, а як оператор.

Використання

$ ./a 2 3 5 7
1 2 3 6 5 10 15 30 7 14 21 42 35 70 105 210

C не суворо визначає порядок оцінювання аргументів. Зокрема, у багатьох викликах функцій C аргументи оцінюються справа наліво.
COTO

З розділу 6.5.2.2 ISO / IEC 9899: TC3 : Порядок оцінки позначувача функції, фактичні аргументи та субекспресії в межах фактичних аргументів не визначено [.] Отже, саме від компілятора, в якому порядку функціонує функція аргументи оцінюються. З -Wsequence-pointабо -WallGCC скаржиться.
Денніс

1. Ви можете змінити c-=1до c--або навіть використовувати , i=--c<<cякщо ви не заперечуєте UB (це , здається, працює з GCC). 2. Обидва варіанти використання ||можна замінити на потрійні оператори: p=i%c?p:!!printf("%d ",p)іp*=(i/c>>i%c)&1?1:atoi(v[i%c+1])
Денніс

@Dennis Дякую за поради! Я розмістив перед сном, щоб я просто запустив програму. c-=1це такий базовий гольф, я не повинен був його пропустити, але це було швидке виправлення помилок, тому що я забув, що в argv (назва програми) є одна додаткова рядок, що i=..c<<cпрацює на GCC / cygwin, але я залишив оригінал програма такою, якою вона є, і перейшла до функції. Тому я щойно дізнався, що sizeofне працює над масивами, переданими як аргументи функції. Я включив ваші пропозиції щодо потрійних операторів у функцію. Я затримався з результатом stdout, оскільки не бачу короткого способу повернути масив.
Рівень Рівер Сент

Так, масиви передаються як аргументи функцій, розпадаються на покажчики. - Нечасто в C передавати вказівник на масив, який повинен містити результати як параметр функції. Питання говорить про те, що ви можете припустити, що продукти менше 2 ^ 31, тому ви можете просто передати масив розміром 512.
Денніс

3

Haskell, 27 байт

Це реалізація Haskell відповіді CJam @ sudo як анонімної функції. Він не переможе приголомшливе рішення Haskell @proud haskeller, але я все одно кину його сюди.

foldr((=<<)(++).map.(*))[1]

Пояснення: foldr приймає двійкову функцію, значення та список. Потім він замінює кожну клітинку мінусів в списку шляхом застосування функції, і кінець списку за значенням, наприклад: foldr f v [a,b,c] == f a (f b (f c v)). Наше значення - це одноелементний список, який містить 1, і двійкова функція f = (=<<)(++).map.(*). Тепер, fприймає число n, робить функцію, (n*)яка помножується на n, робить з нього функцію, g = map(n*)яка застосовує цю функцію до всіх елементів списку, і подає цю функцію до (=<<)(++). Тут (++)є функція конкатенації і (=<<)є монадичним зв'язуванням , яке в даному випадку приймає до її копії і об'єднує їх два.(++) і g, і дає функцію , яка приймає в списку, застосовуєтьсяg

Коротше кажучи: почніть з [1]кожного номера nу списку введення, зробіть копію поточного списку, помножте все на nта додайте його до поточного списку.


3

Пітон: 55 символів

f=lambda l:l and[x*l[0]for x in f(l[1:])]+f(l[1:])or[1]

Рекурсивно генерує продукти, вибираючи по черзі включати або виключати кожне число.


Рекурсивне рішення! Класно!
Тодд Леман

Я думаю, ви можете скинути пробіл після того, andяк будете писати суму навпаки?
mathmandan

@mathmandan Yup, це працює, дякую.
xnor

3

PARI / GP , 26 байт

v->divisors(factorback(v))

Більш тривалі версії включають

v->divisors(prod(i=1,#v,v[i]))

(30 байт) і

v->divisors(fold((x,y)->x*y,v))

(31 байт).

Зауважте, що якщо вхід був матрицею факторизації, а не набором, 18 байтів можна було б зберегти, використовуючи divisorsокремо. Але перетворення набору в матрицю факторизації, здається, займає більше 18 байт. (Я можу це зробити в 39 байтах безпосередньо як v->concat(Mat(v~),Mat(vectorv(#v,i,1)))або 24 байти шляхом множення і повторного факторингу. Хтось v->factor(factorback(v))може зробити краще?)


2

Шавлія - 36 34

По суті, те саме , що рішення Мартіна Бюттнера , якщо я правильно його розумію. Як я вже згадував це в коментарі, я міг би також опублікувати це як відповідь.

lambda A:map(prod,Combinations(A))

Це анонімна функція, яку можна назвати, наприклад, наступним чином:

(lambda A:map(prod,Combinations(A)))([2,3,5,7])

1
Ви можете поголити 2 байти, зробивши це анонімною функцією (це дозволено питанням)
гордий haskeller

2

J (20)

Це вийшло довше, ніж я сподівався чи очікував. Ще: коротше, ніж хаскель!

*/@:^"1#:@i.@(2&^)@#

Використання:

    f=:*/@:^"1#:@i.@(2&^)@#
    f 2 3 5 7
1 7 5 35 3 21 15 105 2 14 10 70 6 42 30 210

Це працює для будь-якого набору чисел, а не лише простих чисел. Також праймери можуть мати необмежений розмір, якщо масив має постфікс x:2 3 5 7x


*/@#~2#:@i.@^#є альтернативою для 14 байт.
милі


1

R, 56 байт

r=1;for(i in 1:length(s))r=c(r,apply(combn(s,i),2,prod))

Я вважаю, що s - це набір (і список). Я впевнений, що це можна зробити ще коротше. Побачу.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.