【问题标题】:generate binary representation of numbers from 1 to 2^k-1生成从 1 到 2^k-1 的数字的二进制表示
【发布时间】:2013-11-17 20:10:10
【问题描述】:

我想获得一个二进制表示的整数列表,范围为 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 非常方便

【问题讨论】:

  • 我真的很惊讶我只能找到与 Prolog 相关的这个问题的“重复”......但这并不能真正帮助您编写 Java 代码。

标签: java algorithm binary set


【解决方案1】:

你可以有如下的东西:

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

n 是您想要的最大数字。

更新

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

更多详情请查看this post

【讨论】:

  • 我真的很想避免使用字符串,因为我以后需要使用这些值来执行一些其他计算,而 BitSet 非常方便
【解决方案2】:

奇怪的问题,一些高级概念,但也有一些逻辑空白。

不过,如果您想要每个值的位集,无论如何都要做同样的事情(如 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 的代码进行了相对较大的更改,以便它返回 all 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

【讨论】:

  • 问题很简单,逻辑有问题:)
  • 将我的答案转换为社区 wiki 随时修改以改进结果,无论如何。
  • 我也打算建议改进递归算法......不想显得有偏见...... ;-)
  • 奇怪的是,对于我的机器(Java 7)上的 6 位以上(按照提供的方式运行程序),递归方法更快。而且我相信,为了获得体面的基准,您应该记录循环前后的结果,而不是在循环的每一步。
  • 能否达到 6 位的 GC 周期?至于时机,是的,但有时 JIT 编译器更有趣的是交错代码,给双方一个平等的“机会”。
【解决方案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);
}

【讨论】:

  • 相当不错。空间探索为一棵树,递归调用长度不超过n-k+1。
  • 我会接受这个答案,因为如果以某种方式符合我的思维过程。虽然@rolfl 的回答也很棒
  • @Maggie 我并不关心谁得到了“分数”的答案,但我应该提到,递归并不是解决这个问题的最有效解决方案......你认为你需要递归来解决的想法这是这个问题的错误“模式”。
  • 为什么?在纸上生成解决方案时,遵循的逻辑是不是类似?
  • @rolfl 我不选择递归方法,因为我认为它会更快,我选择它是因为代码更直观(至少对我而言)并且通常更不容易出错。而且性能差异通常那么不大 - 通常,如果您对结果做任何不重要的事情,您不太可能注意到性能差异。
【解决方案4】:

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

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。

【讨论】:

  • 太棒了!这正是我所需要的。
【解决方案5】:

这里有个简单的解题方法,相当于遍历一棵完整的二叉树,记录每条路径。

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

}

【讨论】:

    【解决方案6】:
    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);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-12-14
      • 1970-01-01
      • 1970-01-01
      • 2023-03-14
      • 2020-02-28
      • 1970-01-01
      • 2022-08-17
      • 2022-11-13
      • 2012-02-10
      相关资源
      最近更新 更多