1

I need to generate all possible boolean states (2^n) of n variables. For example for n=3 it should look like this:

0 0 0  
1 0 0  
0 1 0  
0 0 1  
1 1 0  
1 0 1  
0 1 1  
1 1 1  

Unfortunately I cannot use binary magic, like it is shown here so this (master)piece of simplicity is unusable for me :(

final int n = 3;
for (int i = 0; i < Math.pow(2, n); i++) {
    String bin = Integer.toBinaryString(i);
        while (bin.length() < n)
        bin = "0" + bin;
    System.out.println(bin);
} 

I need to do this in a simple yet efficient way using plain cycles without special things like binary or list functions as shown in the answer above.

Any help would be much appreciated!

Community
  • 1
  • 1
user752637
  • 51
  • 5
  • Why can't you use binary? – ostrichofevil Jan 23 '16 at 21:50
  • Perhaps this helps: http://stackoverflow.com/questions/8374079/fastest-way-to-generate-all-binary-strings-of-size-n-into-a-boolean-array?rq=1 – Jose Luis Jan 23 '16 at 21:55
  • @user752637 The code is sound - what do you mean you can't use binary magic? If you don't want to use the Math.pow function you can simply do `(0x01 << n)` but you still have to traverse the possible states... – Mr Rho Jan 23 '16 at 21:56
  • @MrRho: I cannot use binary because I am porting this to a system where there are no binary types / functions. – user752637 Jan 23 '16 at 23:00
  • @JoseLuis: Unfortunately it uses binary and I can't use it, but it is a great solution! Thanks! – user752637 Jan 23 '16 at 23:26

3 Answers3

2

what about something like this? Add a comment if something is not clear!

void allConfigurations(int n, int[] config, int index)
{
    if(index == n)// base case, the whole config has been filled
    {
        printConfiguration(config);
        return;
    }
    config[index] = 0; //create config with 0 at index
    allConfigurations(n, config, index + 1);
    config[index] = 1; //create config with 1 at index
    allConfigurations(n, config, index + 1);
}
Tommaso Pasini
  • 1,521
  • 2
  • 12
  • 16
  • This is AWESOME! Great recursion! Thanks a lot!!! I wish I could write something like this out of the top of my head. – user752637 Jan 23 '16 at 23:04
  • You have to do a lot of exercise to handle recursion is not something that you just do at first attempt! So just keep coding and exercise! :) – Tommaso Pasini Jan 24 '16 at 08:16
  • Yes, it's like you have to grow it, the recursion comprehension :) I should get a book on recursion or something. – user752637 Jan 24 '16 at 12:58
2

The states are

sub bits {
   my ($n, $i) = @_;
   return map { my $bit = $i & 1; $i >>= 1; $bit } 0..$n-1;
}

my $n = 3;
for my $i (0 .. (1<<$n)-1) {
   my @bits = bits($n, $i);
   print("@bits\n");
}

If you don't have &, << and >> (which would be really really weird as all CPUs have these), or if you want more than two states, you can use the following:

sub bits {
   my ($b, $n, $i) = @_;
   return map { my $bit = $i % $b; $i = ($i-$bit)/$b; $bit } 0..$n-1;
}

my $n = 3;
my $b = 2;
for my $i (0 .. ($b**$n)-1) {
   my @bits = bits($b, $n, $i);
   print("@bits\n");
}

Optimized:

sub get_next {
   my ($b, $bits) = @_;
   for (@$bits) {
      return 1 if ++$_ != $b;
      $_ = 0;
   }
   return 0;
}

my $n = 3;
my $b = 2;
my @bits = (0) x $n;
while (1) {
   print("@bits\n");
   last if !get_next($b, \@bits);
}

Benchmark results for n=3,

                  Rate tommaso_pasini        ikegami ikegami_inline
tommaso_pasini 34462/s             --           -23%           -49%
ikegami        44612/s            29%             --           -34%
ikegami_inline 68061/s            97%            53%             --

Benchmark results for n=10,

                Rate tommaso_pasini        ikegami ikegami_inline
tommaso_pasini 271/s             --           -33%           -58%
ikegami        403/s            48%             --           -38%
ikegami_inline 644/s           138%            60%             --

Benchmarking code:

use strict;
use warnings;
use Benchmark qw( cmpthese );

sub _tommaso_pasini {
    my ($n, $bits, $i) = @_;

    if ($i == $n) {
    # ...
        return;
    }

    $bits->[$i] = 0;
    _tommaso_pasini($n, $bits, $i+1);
    $bits->[$i] = 1;
    _tommaso_pasini($n, $bits, $i+1);
}

sub tommaso_pasini {
   my ($n) = @_;
   _tommaso_pasini($n, [], 0);
}

sub ikegami_next {
   my ($bits) = @_;
   for (@$bits) {
      return 1 if ++$_ != 2;
      $_ = 0;
   }
   return 0;
}

sub ikegami {
   my ($n) = @_;
   my $bits = [ (0) x $n ];
   while (1) {
      # ...
      last if !ikegami_next($bits);
   }
}

sub ikegami_inline {
   my ($n) = @_;
   my $bits = [ (0) x $n ];
   OUTER: while (1) {
      # ...

      for (@$bits) {
         next OUTER if ++$_ != 2;
         $_ = 0;
      }

      last;
   }
}

for my $n (3, 10) {
   cmpthese(-3, {
      ikegami        => sub { ikegami($n)        },
      ikegami_inline => sub { ikegami_inline($n) },
      tommaso_pasini => sub { tommaso_pasini($n) },
   });
}
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • This is also a great solution, the one with the artificial binaries :) Thanks! But I still preffer the first solution above, because it gives me the opportunity to parametrize also the quantity of states. At some point I can say - now I don't have only boolean states, but I have let's say 4 states - true, false, half-true and half-false. I know it sounds like "just a little pregnant" but for me it makes sense :) – user752637 Jan 24 '16 at 12:31
  • OK, I just did that with your solution also. Many paths lead to Rome I guess :) – user752637 Jan 24 '16 at 12:52
  • If you want ternary, just replace 2** with 3**, %2 with %3, and /2 with /3. – ikegami Jan 24 '16 at 15:26
  • Added an optimized version. – ikegami Jan 24 '16 at 17:49
  • Thanks! But still the best option, performance wise is the one with the recursion above. It delivers the result almost twice as fast. – user752637 Jan 31 '16 at 19:55
  • @user752637, What? No,that's not true. Mine is 30% faster for n=3 and 50% faster for n=10. In fact, if you inline `get_next`, mine is the one that's twice as fast (n=3) or even faster (2.4x for n=10). Subroutine calls are very expensive in Perl. Benchmarks added to answer. – ikegami Jan 31 '16 at 20:18
1

Just posting the Perl version of the great solution above, in case somebody needs it. @TommasoPasini thanks again!

sub allConfigurations{
    my ($n, $index, @config) = @_;

    if($index == $n) #base case, the whole config has been filled
    {
        &printConfiguration(@config);
        return;
    }
    $config[$index] = 0; #create config with 0 at index
    allConfigurations($n, $index + 1, @config);

    $config[$index] = 1; #create config with 1 at index
    allConfigurations($n, $index + 1, @config);
}

sub printConfiguration{
    my @config = @_;

    print "@config";
    print "\n";
}


&allConfigurations(3, my $index=0, my @conf);
user752637
  • 51
  • 5