От 0 до 2 ^ n - 1 в порядке попкорна


18

... Ах, извините, здесь нет попкорна, просто POPCNT.

Напишите самую короткую программу или функцию, которая принимает число, nи выведите все целые числа от 0 до 2 n - 1 в порядке возрастания числа 1 бит в двоичном представлении чисел (popcount). Дубликаты не допускаются.

Порядок чисел с одним и тем же popcount определяется реализацией.

Например, для n = 3, все эти выходные данные действительны:

0, 1, 2, 4, 3, 5, 6, 7
[0, 4, 1, 2, 5, 3, 6, 7]
0 4 2 1 6 5 3 7 

Формат ввода и вывода определяется реализацией, что позволяет использовать возможности языка для дальнейшего совершенствования кода. Есть несколько ограничений на вывод:

  • Числа должны быть выведены в десятичном формате.
  • Выходные данные должны содержать разумный разделитель между числами (конечный разделитель разрешен, но не ведущий).

    Линия подачи ( \n), вкладка ( \t), пространство, ,, ., ;, |, -, _, /вполне разумны сепаратор. Я не против дополнительных мест для красивой печати, но не используйте буквы или цифры в качестве разделителей.

  • Номера и сепараторы могут быть окружены [ ], { }или любого массива или списка обозначений.
  • Не печатайте ничего, что не указано выше.

бонус

Умножьте ваш счет на 0,5, если ваше решение может генерировать число на лету. Суть этого бонуса заключается в том, что если вы должны были напрямую преобразовать свое решение для печати в генератор, генератор использует не более O (n) памяти, где n - это количество бит, как определено выше. (Вам не нужно конвертировать ваше решение в генератор). Обратите внимание, что, хотя я навязываю n <= 28, память, необходимая для хранения всех чисел, по-прежнему растет в геометрической прогрессии, и простое решение для сортировки потребовало бы минимум 4 ГБ памяти при n = 28.

Пожалуйста, добавьте простое объяснение того, как работает ваше решение, прежде чем претендовать на этот бонус.


4
Кажется, что задача, как это довольно скучно и приведет к куче сортировки ответов. Я хотел бы добавить бонус, чтобы сделать задачу более интересной. Что-то по линии «генерации чисел на лету». Если вы согласны с этим, пожалуйста, подпишите этот комментарий, и я добавлю его к вопросу.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Если вы не согласны, пожалуйста, подпишите этот комментарий.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

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

21
@JanDvorak: Это было в песочнице в течение одного месяца.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
Я думаю, что уже слишком поздно для этого вопроса. В общем, вопросы, по которым вы должны выяснить нетривиальный алгоритм, на мой взгляд, не очень подходят для гольф-кода. Вместо этого сделайте им вызов кода и создайте все необходимые ограничения.
FUZxxl

Ответы:



7

Python 2, 75 * 0,5 = 37,5

N=2**input()-1
v=N-~N
while v:t=1+(v|~-v);v=N&t|~-(t&-t)/(v&-v)/2;print v^N

Повторно генерирует следующий наивысший vс тем же POPCOUNT по этому алгоритму битовой перестановки .

На самом деле оказалось, что их легче сгенерировать с уменьшением количества попсов, а затем распечатать дополнение, чтобы увеличить его. Таким образом, затем происходит vпереполнение 2**n, мы просто удаляем все, кроме nбитов, &Nгде N=2**n-1, и это дает наименьшее число на один попконт ниже. Таким образом, мы можем просто сделать один цикл. Вероятно, есть лучшее решение, которое напрямую находит следующий более низкий номер с тем же POPCOUNT.

Из-за проблемы, связанной с забором, нам нужно начать с v=2**(n+1)-1того, чтобы операция производилась v=N-1в первом цикле.

Выход для 4:

0
8
4
2
1
12
10
9
6
5
3
14
13
11
7
15

Нет необходимости увеличивать число с одним и тем же поп-счетом. Порядок определяется реализацией.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
@ n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ Я знаю, но не понимаю, как сохранить персонажей, делая это по-другому.
xnor

При наивном методе с 3 петлями я набираю почти одинаковые результаты в JS (имея console.log()против print). Может быть, хитрость слишком тяжелая.
edc65

Сохранение одного байта:v=N-~N
Sp3000

5

J, 19 символов, без бонуса.

[:(/:+/"1@#:)@i.2^]
  • 2 ^ y- двое во власти y.
  • i. 2 ^ y- целые числа от 0до (2 ^ y) - 1.
  • #: i. 2 ^ y - каждое из этих целых чисел представлено в базе два.
  • +/"1 #: i. 2 ^ y - суммы каждого представления
  • (i. 2 ^ y) /: +/"1 #: i. 2 ^ y- вектор i. 2 ^ yотсортирован по порядку элементов предыдущего вектора, наш ответ.

3

Питон, 63 символа

F=lambda n:`sorted(range(1<<n),key=lambda x:bin(x).count('1'))`

>>> F(3)
'[0, 1, 2, 4, 3, 5, 6, 7]'

@ Алекс: Список ограничений подразумевал, что он хотел получить строковый результат.
Кит Рэндалл

Извините, пропустил это.
Алекс А.

3

С 179 * 0,5 = 89,5

main(){int n,i=0,m,o;scanf("%d",&n);m=~((~0)<<n);for(;n--;++i){for(o=0;o<m;++o){int bc=0,cb=28;for(;cb--;)bc+=o&(1<<cb)?1:0;if(bc==i)printf("%d ",o);}}printf("%d\n",m);return 0;}

РЕДАКТИРОВАТЬ: 157 * 0,5 = 78,5

main(){int n,i=0,m,o;scanf("%d",&n);m=~((~0)<<n);for(++n;n--;++i){for(o=0;o<=m;++o){int bc=0,cb=28;for(;cb--;)bc+=o&(1<<cb)?1:0;if(bc==i)printf("%d ",o);}}}

РЕДАКТИРОВАТЬ: 132 * 0,5 = 66

main(){int n,i=0,m,o;scanf("%d",&n);m=~((~0)<<n);for(++n;n--;++i){for(o=0;o<=m;++o){if(__builtin_popcount(o)==i)printf("%d ",o);}}}

или немного лучше отформатирован:

main()
{
    int n, i = 0, m, o;
    scanf("%d", &n);
    m = ~((~0) << n);
    for(++n; n--; ++i)
    {
        for(o = 0; o <= m; ++o)
        {
            if (__builtin_popcount(o) == i)
                printf("%d ", o);
        }
    }
}

Что оно делает?

m = ~((~0) << n);

вычисляет последнее отображаемое число (pow (2, n) - 1)

    for(++n; n--; ++i)
    {
        for(o = 0; o <= m; ++o)
        {

внешний цикл перебирает счетчик битов (от 0 до n-1), в то время как внутренний цикл просто отсчитывает от 0 до m

            if (__builtin_popcount(o) == i)
                printf("%d ", o);

На x86 есть инструкция POPCNT, которую можно использовать для подсчета установленных битов. GCC и совместимые компиляторы могут поддерживать функцию __builtin_popcount, которая в основном компилируется в эту инструкцию.




1

JavaScript (ES6) 41 (82 * 0,5)

Самый простой способ, игра в гольф

F=b=>{
  for(l=0;l<=b;l++)
    for(i=1<<b;i;t||console.log(i))
      for(t=l,u=--i;u;--t)
        u&=u-1;
}

Ungolfed

F=b=>
{
  for (l = 0; l <= b; l++)
  {
    for (i = 1 << b; i > 0; )
    {
      --i;
      for (t = 0, u = i; u; ++t) // Counting bits set, Brian Kernighan's way
        u &= u - 1;
      if (t == l) console.log(i);
    }
  }
}

Тест в консоли Firefox / FireBug

F(4)

0
8
4
2
1
12
10
9
6
5
3
14
13
11
7
15


1

Bash + coreutils, 66

Один, чтобы вы начали:

jot -w2o%dpc $[2**$1] 0|dc|tr -d 0|nl -ba -v0 -w9|sort -k2|cut -f1

Здесь нет ничего захватывающего. Учитывая ваш комментарий, я с удовольствием удалю / пересмотрю этот ответ, если вы хотите изменить вопрос.
Цифровая травма

Не уверен, стоит ли выделять, ваша программа должна работать для всех значений n от 0 до 28 включительно. Я не знаю, сколько ответов здесь отвечают этому требованию.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Я удалил пункт, так как люди, кажется, все равно его не замечают.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@ n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ Теперь, по крайней мере, теоретически это должно работать до 28. Я до сих пор тестировал до 22, но, конечно, это sortзанимает много времени. При n = 28 sortнужно будет отсортировать 2 ^ 28 строк / ~ 13 ГБ данных.
Цифровая травма

1

Haskell, (87 * 0,5) = 43,5

f n=[0..n]>>=(\x->x#(n-x))
a#0=[2^a-1]
0#_=[0]
a#b=[1+2*x|x<-(a-1)#b]++[2*x|x<-a#(b-1)]

Пример использования: f 4который выводит[0,1,2,4,8,3,5,9,6,10,12,7,11,13,14,15]

Как это работает: ни сортировка, ни многократная итерация по [0..2 ^ n-1] и поиск чисел, содержащих i 1s.

В #функции помощника принимает два параметра aи bи формирует список каждого числа, состоящие из a1s и b0s. Основная функция fтребует, #чтобы каждая комбинация aи bгде a+bравнялась n, начиная с 1 и n0, чтобы числа были в порядке. Благодаря лени Хаскелла все эти списки не должны создаваться полностью в памяти.


Имеет ли не ++в a#bозначают , что левая рука (которая может быть большим) должен быть произведен полностью , а затем копируется перед первым элементом в результате получается, нарушив тем самым требования к бонусом?
Жюль

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

1

Руби 47 символов

Очень похоже на Python из @KeithRandall:

f=->n{(0..1<<n).sort_by{|x|x.to_s(2).count ?1}}

1

Математика, 26

Tr/@(2^Subsets@Range@#/2)&

Пример:

Tr/@(2^Subsets@Range@#/2)&[4]

{0, 1, 2, 4, 8, 3, 5, 9, 6, 10, 12, 7, 11, 13, 14, 15}


0

Perl, 64/2 = 32

#!perl -ln
for$i(0..$_){$i-(sprintf"%b",$_)=~y/1//||print for 0..2**$_-1}

Просто переберите интервал [0..2^n-1] n + 1времени. В каждой итерации выведите только те числа, у которых число в 1 бите равно переменной итерации ( $i). Биты подсчитываются путем подсчета 1's (y/1// ) в числе, преобразованном в двоичную строку с sprintf.

Тестовое задание меня .

Perl, 63

Сортировка подход:

#!perl -l
print for sort{eval+(sprintf"%b-%b",$a,$b)=~y/0//dr}0..2**<>-1

1
@Optimizer, он использует O (1) памяти. Какое еще определение у нас есть? К сожалению, это не так, поскольку я печатаю это вживую :)
nutki

@ Оптимизатор, исправлено.
Nutki

Что ж, я знаю об этом, когда устанавливаю условие, но все равно разрешаю, поскольку хочу посмотреть, какие запутанные ответы могут дать люди.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

2
Я просто спросил «как», так как я не могу читать Perl :) Хотите добавить больше объяснений?
Оптимизатор

@ Оптимизатор, добавлено еще несколько объяснений.
nutki

0

Ява 8, 205

public class S{public static void main(String[] n){java.util.stream.IntStream.range(0,1<<Integer.parseInt(n[0])).boxed().sorted((a,b)->Integer.bitCount(a)-Integer.bitCount(b)).forEach(System.out::print);}}

0

C ++ 11, 117 символов:

using namespace std;int main(){ set<pair<int,int> > s;int b;cin>>b;int i=0;while(++i<pow(2,b))s.insert({bitset<32>(i).count(),i});for (auto it:s) cout <<it.second<<endl;}

Ungolfed:

using namespace std;
int main()
{
    set<pair<int,int> > s;
    int b;
    cin>>b;
    int i=0;
    while (++i<pow(2,b))  {
        s.insert({bitset<32>(i).count(),i});
    }
    for (auto it:s) {
        cout <<it.second<<endl;
    }
}

Объяснение:

Создайте набор пар int, int. Первое int - это число бит, второе - число. Пары сравнивают себя по первому параметру, поэтому набор сортируется по количеству бит.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.