生成从1到2 ^ k-1的数字的二进制表示

时间:2013-11-17 20:10:10

标签: java algorithm binary set

我想获得范围为1 ... 2 ^ k-1的二进制表示的整数列表。或者,我需要一个k位的所有可能组合的列表。所以,例如,假设k是3我需要:

001
010
011
100
101
110
111

由于我计划在Java中执行此操作,我正在考虑使用BitSet来表示每个数字,并将值存储在列表中,但我认为这个问题可能与语言无关。基本上,我需要弄清楚生成整个集合的算法。

我认为我需要一个递归解决方案,这会考虑先前设置的位。

void fill(int k, int i, boolean wasSet) {
    if (i==k) return;
        BitSet bs = new BitSet();
        for (int j=0; j<k; j++) {
          if (!wasSet) {
              bs.set(i);
              fill(k, i, true);
          } else {
              fill(k, j, false);
          }
    }

注意:这个函数原型显然是非常错误的。

注意2:我真的很想避免使用字符串,因为我后来需要使用这些值来执行其他一些计算,而BitSet对此非常方便

6 个答案:

答案 0 :(得分:6)

您可以拥有以下内容:

for(int i=0 ; i<n ; i++){
  System.out.println(Integer.toBinaryString(i));
}

其中n是您想要的最大数字。

更新

要使用BitSet,java 7会有BitSet.valueOf(byte[])BitSet.toByteArray()

有关详细信息,请查看this post

答案 1 :(得分:4)

奇怪的问题,一些先进的概念,但也有一些逻辑上的差距。

但是,如果你想要每个值的位集,那么做同样的事情(就像tokhi建议的那样):

int size = 1 << bits;
ArrayList<BitSet> results = new ArrayList<>(size);
for (int val = 1; val < size; val++) {
    BitSet bs = new BitSet(bits);
    results.add(bs);
    for (int b = 0; b < bits; b++) {
        if ( ((val >>> b) & 1) == 1) {
           bs.set(b);
        }
    }
}

修改

在关于递归或循环是否更好的'聊天'之后,我已经把这个测试放在一起......

我已经修改了上面的代码以提高效率,但是,我对Dukeling的代码进行了相对较大的更改,以便它返回所有 BitSets而不是仅修改一个而不存储结果。

请注意,递归代码中存在“错误”,因为它返回的no-bits-set值不应该是结果的一部分......

无论如何,这只是值得深思的事。

这是我的测试代码:

import java.util.ArrayList;
import java.util.BitSet;

public class Junk {

    private static final ArrayList<BitSet> loop(final int bits) {
        int size = 1 << bits;
        ArrayList<BitSet> results = new ArrayList<>(size);
        for (int val = 1; val < size; val++) {
            BitSet bs = new BitSet(bits);
            results.add(bs);
            int v = val;
            int b = 0;
            while (v != 0) {
                if ( (v & 1) == 1) {
                   bs.set(b);
                }
                b++;
                v >>>= 1;
            }
        }
        return results;
    }

    private static final ArrayList<BitSet> recurse(final int bits) {
        ArrayList<BitSet> retval = new ArrayList<BitSet>();
        BitSet bitset = new BitSet(bits);
        fill(bitset, 0, bits, retval);
        return retval;
    }


    private static final void fill(final BitSet bs, final int k, final int n, final ArrayList<BitSet> results)
    {
       if (k == n) {
          results.add((BitSet)bs.clone());
          return;
       }
       bs.set(k, false);
       fill(bs, k+1, n, results);
       bs.set(k, true);
       fill(bs, k+1, n, results);
    }

    private static final void exercise(final int bits) {

        double acnt = 0;
        double bcnt = 0;
        long atime = 0L;
        long btime = 0L;

        for (int i = 0; i < 1000; i++) {
            final long as = System.nanoTime();
            acnt += recurse(bits).size();
            atime += System.nanoTime() - as;
            final long bs = System.nanoTime();
            bcnt += loop(bits).size();
            btime += System.nanoTime() - bs;
        }

        acnt /= 1000;
        bcnt /= 1000;

        System.out.printf("    Bits %d: ms/call -> recurse %.3fms loop %3fms (recurse %.1f/%d loop %f.1/%d\n",
                bits, atime / 1000000.0, btime / 1000000.0, acnt, 1<<bits, bcnt, (1 << bits) - 1);
    }

    public static void main(String[] args) {
        System.out.println("warmup");

        exercise(3);
        exercise(2);
        exercise(1);

        System.out.println("real runs");

        exercise(1);
        exercise(2);
        exercise(3);
        exercise(4);
        exercise(5);
        exercise(6);
        exercise(7);
        exercise(8);
        exercise(9);
        exercise(10);
        exercise(11);

    }

}

这是我机器上的输出:

warmup
Bits 3: ms/call -> recurse 12.324ms loop 7.109403ms (recurse 8.0/8 loop 7.000000.1/7
Bits 2: ms/call -> recurse 2.949ms loop 2.392226ms (recurse 4.0/4 loop 3.000000.1/3
Bits 1: ms/call -> recurse 1.681ms loop 1.038053ms (recurse 2.0/2 loop 1.000000.1/1
real runs
Bits 1: ms/call -> recurse 1.743ms loop 0.865739ms (recurse 2.0/2 loop 1.000000.1/1
Bits 2: ms/call -> recurse 1.996ms loop 0.261967ms (recurse 4.0/4 loop 3.000000.1/3
Bits 3: ms/call -> recurse 3.150ms loop 0.544942ms (recurse 8.0/8 loop 7.000000.1/7
Bits 4: ms/call -> recurse 4.876ms loop 0.932031ms (recurse 16.0/16 loop 15.000000.1/15
Bits 5: ms/call -> recurse 6.128ms loop 1.775841ms (recurse 32.0/32 loop 31.000000.1/31
Bits 6: ms/call -> recurse 9.937ms loop 3.209335ms (recurse 64.0/64 loop 63.000000.1/63
Bits 7: ms/call -> recurse 21.005ms loop 7.221974ms (recurse 128.0/128 loop 127.000000.1/127
Bits 8: ms/call -> recurse 38.715ms loop 16.410275ms (recurse 256.0/256 loop 255.000000.1/255
Bits 9: ms/call -> recurse 69.904ms loop 41.330404ms (recurse 512.0/512 loop 511.000000.1/511
Bits 10: ms/call -> recurse 132.053ms loop 88.952120ms (recurse 1024.0/1024 loop 1023.000000.1/1023
Bits 11: ms/call -> recurse 255.921ms loop 193.370808ms (recurse 2048.0/2048 loop 2047.000000.1/2047

答案 2 :(得分:3)

以下是我提出的递归解决方案。

简单地说,对于每个位置,都试图设置该位,然后进行递归。

它对所有排列使用相同的BitSet。如果您希望每个都有一个,您可能需要复制它。

static BitSet bs = new BitSet(3);   
static void fill(int k, int n)
{
   if (k == n)
   {
      System.out.println(bs);
      return;
   }
   bs.set(k, false);
   fill(k+1, n);
   bs.set(k, true);
   fill(k+1, n);
}

public static void main(String[] args)
{
    fill(0, 3);
}

答案 3 :(得分:1)

嗯,你总是可以手动增加:

int k = 3; //or something else

ArrayList<Boolean[]> combinations = new ArrayList<>();

boolean[] current;

void increment() {
    for(int i = 0; i<k; i++) {
        if(current[i]) {
            current[i] = false;
        } else {
            current[i] = true;
            break;
        }
    }
}

void fill() {
    current = new boolean[k];
    combinations.add(current);
    final int max = (int) Math.pow(2, k);
    for(int i = 1; i< max; i++) {
        current = current.clone(); //not sure about this -> google java array copy/clone
        increment();
        combinations.add(current);
    }
}

这会将LSB(最低有效位)置于地址0,但由于它包含所有组合,这应该无关紧要,只有当你将其表示为MSB(大多数 - || - )时才会对它进行排序在索引0。

答案 4 :(得分:0)

这是解决问题的简单方法,它相当于遍历完整的二叉树并记录每条路径。

public class Combinations {

    public static void combinations(int i,int k,char[]buff) {
        if(i<k) {

            buff[i] = '0';
            combinations(i+1,k,buff);
            buff[i] = '1';
            combinations(i+1,k,buff);
        }
        else System.out.println(String.valueOf(buff));
    }

    public static void main(String[] args) {
        int k = 3;
        combinations(0,k,new char[k]);
    }

}

答案 5 :(得分:0)

import java.util.LinkedList;
import java.util.Queue;

public class GenerateBNo 
{
    static void generatePrintBinary(int n)
    {
        Queue<String> q = new LinkedList<String>();
        q.add("1");
        while(n-- > 0)
        {
            String s1 = q.peek();
            q.remove();
            System.out.println(s1);
            String s2 = s1;
            q.add(s1 + "0");
            q.add(s2 + "1");
        }
    }
    public static void main(String[] args) 
    {
        int n=10;
        generatePrintBinary(n);
    }
}