Find the binarray!

24

2

We define a binarray as an array satisfying the following properties:

  • it's non-empty
  • the first value is a 1
  • the last value is a 1
  • all other values are either 0 or 1

For instance, the array [ 1, 1, 0, 1 ] is a valid binarray.

The task

Given a non-empty array A of non-negative integers and a positive integer N, your job is to find a binarray B of length N which allows to generate A by summing an unrestricted number of copies of B, shifted by an unrestricted number of positions.

Example

A = [ 1, 1, 2, 4, 1, 2, 2, 1, 0, 1, 0, 1, 1, 0, 1 ]
N = 4

For this input, the binarray B = [ 1, 1, 0, 1 ] would be a valid answer because we can do:

  [ 1, 1, 0, 1 ]
+       [ 1, 1, 0, 1 ]
+       [ 1, 1, 0, 1 ]
+          [ 1, 1, 0, 1 ]
+                   [ 1, 1, 0, 1 ]
+                                  [ 1, 1, 0, 1 ]
  -----------------------------------------------
= [ 1, 1, 2, 4, 1, 2, 2, 1, 0, 1, 0, 1, 1, 0, 1 ]

Rules

  • Input can be taken in any reasonable format.
  • Output can be either a native array (e.g. [1, 1, 0, 1]) or a binary string with or without a separator (e.g. "1,1,0,1" or "1101")
  • You're only required to print or return one valid binarray. Alternatively, you may choose to print or return all of them when several solutions exist.
  • You are not required to support inputs that do not lead to any solution.
  • The sum may include implicit zeros which do not overlap with any copy of B. The second zero in the above sum is such an implicit zero.
  • Your can assume that the maximum size of A is 100 and the maximum size of B is 30.
  • This is code-golf, so the shortest answer in bytes wins. Standard loopholes are forbidden.

Test cases

Input : N = 1 / A = [ 1, 2, 3, 4, 5 ]
Output: [ 1 ]

Input : N = 2 / A = [ 1, 2, 100, 99 ]
Output: [ 1, 1 ]

Input : N = 3 / A = [ 1, 1, 1 ]
Output: [ 1, 1, 1 ]

Input : N = 3 / A = [ 1, 1, 3, 2, 2 ]
Output: [ 1, 1, 1 ]

Input : N = 3 / A = [ 1, 0, 2, 1, 1, 1, 0, 0, 1, 0, 1 ]
Output: [ 1, 0, 1 ]

Input : N = 4 / A = [ 1, 2, 2, 2, 1 ]
Output: [ 1, 1, 1, 1 ]

Input : N = 4 / A = [ 1, 1, 2, 4, 1, 2, 2, 1, 0, 1, 0, 1, 1, 0, 1 ]
Output: [ 1, 1, 0, 1 ]

Input : N = 4 / A = [ 1, 1, 0, 2, 1, 0, 1 ]
Output: [ 1, 0, 0, 1 ] or [ 1, 1, 0, 1 ]

Input : N = 5 / A = [ 1, 3, 6, 9, 8, 6, 3, 4 ]
Output: [ 1, 1, 1, 0, 1 ]

Input : N = 8 / A = [ 2, 1, 0, 2, 3, 3, 1, 2, 1 ]
Output: [ 1, 0, 0, 1, 1, 1, 0, 1 ]

Input : N = 10 / A = [ 1, 2, 1, 2, 2, 1, 3, 3, 3, 2, 3, 0, 2, 1, 1, 0, 1 ]
Output: [ 1, 1, 0, 1, 0, 1, 1, 1, 0, 1 ]

Input : N = 13 / A = [ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 ]
Output: [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ]

Input : N = 5 / A = [ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 ]
Output: [ 1, 1, 1, 1, 1 ]

Input : N = 6 / A = [ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 ]
Output: [ 1, 0, 0, 0, 0, 1 ]

Input : N = 7 / A = [ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 ]
Output: [ 1, 1, 0, 0, 0, 1, 1 ]

Input : N = 9 / A = [ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 ]
Output: [ 1, 0, 1, 0, 1, 0, 1, 0, 1 ]

Arnauld

Posted 2017-03-30T10:10:01.960

Reputation: 111 334

What's the largest value of N that should reasonably be supported? – Neil – 2017-03-30T10:50:16.447

@Neil I've added size limits on both A and B. – Arnauld – 2017-03-30T11:01:26.953

If you treat the array as each element to the power of two it is find an odd divisor between 2^n-1 and 2^n – fəˈnɛtɪk – 2017-03-30T13:05:01.410

1@fəˈnɛtɪk Maybe, but for N=4, A = [ 1, 1, 2, 4, 1, 2, 2, 2, 1, 2, 2, 1, 2, 0, 1 ], you get 30459 which is divisible by both 11 and 13 yet only one of [ 1, 1, 0, 1 ] and [ 1, 0, 1, 1 ] is a valid answer. – Neil – 2017-03-30T15:28:06.440

1@fəˈnɛtɪk These numbers aren't written in base 2 so rules of arithmetic don't apply. For instance, you explicitly cannot carry when adding. – BallpointBen – 2017-03-30T22:46:53.127

2Please add these test cases, which seem to break nearly all the posted answers: N = 3, A = [1, 0, 2, 0, 2, 0, 1], output = [1, 0, 1]; N = 3, A = [1, 1, 1, 0, 0, 0, 1, 1, 1], output = [1, 1, 1]. – Anders Kaseorg – 2017-06-02T19:38:29.577

Answers

8

PHP, 105 92 90 86 bytes

Jörg´s solution fixed and golfed:

for($b=1+2**$argv[1];;)--$argc>1?$s+=$argv[$argc]*2**$i++:$s%($b-=2)||die(decbin($b));

takes N from first command line argument, values after that; run with -ror test it online.
prints binary number (format 10001); prints invalid solution or runs dead if there is no valid solution.

first version (now 97 bytes) that prints nothing for invalid input: test it online

for($b=1+$m=2**$argv[1];$m/2<=$b;)--$argc>1?$s+=$argv[$argc]*2**$i++:$s%($b-=2)||die(decbin($b));

breakdown

for($b=1+$m=2**$argv[1];$m/2<=$b;)  # second loop: loop $b from 2^N-1 by -2 to 2^(N-1)
--$argc>1                           # first loop: decrease $argc ...
    ?$s+=$argv[$argc]*2**$i++           # while $argc>1: binary sum from last to 2nd argument
    :$s%($b-=2)||die(decbin($b));       # later: if $b divides $s, print in binary and exit

Titus

Posted 2017-03-30T10:10:01.960

Reputation: 13 814

Nice could you not reach a byte count under 100? – Jörg Hülsermann – 2017-03-30T16:37:21.850

1@JörgHülsermann I could. – Titus – 2017-03-31T00:03:29.800

Heavy Thinking. I know before this that you are better. I hope you can hold the lowest byte count – Jörg Hülsermann – 2017-03-31T00:17:24.310

1

On N = 3, A = [1, 0, 2, 0, 2, 0, 1], this incorrectly returns 111 where the only correct result is [1, 0, 1].

– Anders Kaseorg – 2017-06-02T18:30:48.693

8

PHP, 219 bytes

<?for(list($g,$z)=$_GET,$d=~-$l=2**$z;$d>=$l/2;max(array_diff_assoc($r,$g)?:[0])?:$o[]=$b,$d-=2)for($r=[],$b=decbin($d),$k=0;$k<count($g);$k++)for($u=$g[$k]-$r[$k],$i=0;$i<$z;$i++)$u<1?:$r[$k+$i]+=$u*$b[$i];print_r($o);

Try it online!

-4 Bytes using [$g,$z]=$_GET PHP 7.1 instead of list($g,$z)=$_GET

Jörg Hülsermann

Posted 2017-03-30T10:10:01.960

Reputation: 13 026

It seems like it outputs both a valid ([1,0,1,0,1,0,1,0,1]) and an invalid answer ([1,0,0,0,1,0,1,1,1]) for the last test case. – Arnauld – 2017-03-30T12:30:23.443

-8 bytes: while($_GET[0])$s+=2**$i++*array_pop($_GET[0]);. -5 bytes: range(1|.5*$m=2**$_GET[1],$m,2). – Titus – 2017-03-30T13:22:58.080

@Arnauld Yes I should give as Output only the highest binarray too make this solution valid – Jörg Hülsermann – 2017-03-30T13:30:28.897

@Arnauld it is a valid answer. The initial array is equivalent to 101111111101 in binary which is 3069, 100010111 is equivalent to 279, which goes into 3069 11 times – fəˈnɛtɪk – 2017-03-30T13:33:06.500

(+5)-11 bytes: for($b=2**$_GET[1]/2-1|1;2**$l>$b+=2;) (|1 to fix N=1); another -6 if you go from high to low: for($b=1+$m=2**$_GET[1];$m/2<$b-=2;). -4 bytes with -r and $argv instead of $_GET: while(--$argc>1)$s+=2**$i++*array_pop($argv); (N as first argument, array values after that). – Titus – 2017-03-30T13:45:26.953

2@fəˈnɛtɪk I do agree with your maths, but the challenge is about finding a pattern that can be summed exactly to A, not an equivalent arrangement. Here, we'd get [ 1,0,1,1,1,0,2,2,2,2,2,1 ]. – Arnauld – 2017-03-30T13:45:31.463

@Titus feel free to add your solution or make an own post. Sometimes if you golfed a version from me down I could not really understand your ideas – Jörg Hülsermann – 2017-03-30T14:08:14.453

1-1 byte with for($g=$_GET[0];$g;). – Titus – 2017-03-30T15:29:04.430

-1 byte with while($g=&$_GET[0]) – Titus – 2017-04-17T23:34:34.333

3

Python, 166 bytes

def f(a,n):
 for i in range(1<<n-1,1<<n):
  b=bin(i)[2:];u,v=(int(('0{:0>%d}'%sum(a)*len(s)).format(*s))for s in[a,b])
  if u%v<1>int(str(u//v*10)[::~sum(a)]):yield b

Try it online!

How it works

Consider A and B as the digits of base k numbers u and v. For example (we’ll use k = 1000 for illustration):

A = [1, 2, 1, 3, 2, 1, 2]
B = [1, 0, 0, 1]
u = 1 002 001 003 002 001 002
v = 1 000 000 001

As many of the other answerers noticed, if B is a valid answer, then u is divisible by v. In this case,

u = 1 002 001 002 ⋅ v

This quotient, translated back to the array [1, 2, 1, 2], tells us exactly how many copies of B we need shifted to each position.

  [1, 0, 0, 1]
+    [1, 0, 0, 1]
+    [1, 0, 0, 1]
+       [1, 0, 0, 1]
+          [1, 0, 0, 1]
+          [1, 0, 0, 1]
-----------------------
  [1, 2, 1, 3, 2, 1, 2]

(Why? Because that is exactly how long multiplication works in base k.)

What the other answerers failed to notice is that the above condition is not sufficient. For example:

A = [1, 2, 1, 3, 2, 1, 2]
B = [1, 1, 1, 1]
u = 1 002 001 003 002 001 002
v = 1 001 001 001
u = 1 000 999 002 ⋅ v

Mathematically speaking, we can still translate that quotient back to the array [1, 1, −1, 2], which works fine if we’re allowed to use negative copies of B:

  [1, 1, 1, 1]
+    [1, 1, 1, 1]
−       [1, 1, 1, 1]
+          [1, 1, 1, 1]
+          [1, 1, 1, 1]
-----------------------
  [1, 2, 1, 3, 2, 1, 2]

but of course the challenge does not permit negative copies. So we need an additional check.

Toward that end, we select a base k = 10e where k > 10 ⋅ sum(A), and check that none of the base k digits overflow into the next base k digit when we multiply the quotient by ten. That is, every eth base ten digit, starting at the end, in the base ten representation of the quotient times ten, must be 0. This guarantees that the quotient translates back to an array with nonnegative elements.

Anders Kaseorg

Posted 2017-03-30T10:10:01.960

Reputation: 29 242

1I love your trick of using a large power of 10 as the base to make the base conversion easier. – Neil – 2017-06-04T20:38:35.257

2

PHP, 213 Bytes

Same way a little bit golfed

<?for($b=2**~-$l=$_GET[1];$b<2**$l;array_filter($t[$b++])?:$d[]=$o)for($g=count($t[$b]=$_GET[$i=0]);min($t[$b])>-1&$i<=$g-$l;$i++)for($e=$t[$b][$i],$k=0,$o=decbin($b);$k<$l;)$t[$b][$k+$i]-=$o[$k++]*$e;print_r($d);

Try it online!

PHP, 344 Bytes first working

After my first answer I have decide to make a longer try that give back all valid solutions.

<?foreach(range(2**($l=$_GET[1])-1,2**($l-1))as$b){$t[$b]=($g=$_GET[0]);for($i=0;$t[$b]&&$i<=count($g)-$l;$i++){$e=reset($y=array_slice($t[$b],$i,$l));foreach(str_split(decbin($b))as$k=>$v)$t[$b][$k+$i]=$y[$k]-$e*$v;if(min($t[$b])<0)unset($t[$b]);}}foreach($t as$k=>$v)if(max($v)>0)unset($t[$k]);echo join(",",array_map(decbin,array_keys($t)));

Online Version

Breakdown

foreach(
    range(2**($l=$_GET[1])-1
    ,2**($l-1)
    ) # make decimal range of a binarray with given length
    as$b){
$t[$b]=($g=$_GET[0]); # make a copy for each possible solution pattern
    for($i=0;$t[$b]&&$i<=count($g)-$l;$i++){ # Loop till solution is valid or reach last digit
        $e=reset($y=array_slice($t[$b],$i,$l)); # take first value of a sequence with the length
        foreach(str_split(decbin($b))as$k=>$v)
            $t[$b][$k+$i]=$y[$k]-$e*$v; # replace values in copy
        if(min($t[$b])<0)unset($t[$b]); # kill solution if a minimum <0 exists
    }
}
foreach($t as$k=>$v)if(max($v)>0)unset($t[$k]); # drop all solutions where the sum is not zero 


echo join(",",array_map(decbin,array_keys($t))); #Output all solutions

Jörg Hülsermann

Posted 2017-03-30T10:10:01.960

Reputation: 13 026

This seems to work for N ≥ 2, but fails on N = 1 cases, such as the first test case in the challenge. – Anders Kaseorg – 2017-06-04T02:35:00.573

@AndersKaseorg Now it supports the N = 1 Cases it needs only to set a = in the first loop for the shorter version In the greater version it needs to delete four Bytes – Jörg Hülsermann – 2017-06-04T11:00:14.633

1

Python, 205 bytes

def f(a,l):
 b=lambda s:b(s[:-1])*sum(a)*8+int(s[-1])if s else 0
 c=lambda n:n and(n/sum(a)/4%2 or c(n/sum(a)/8))
 for i in range(2**~-l,2**l):
  j=bin(i)[2:]
  if b(a)%b(j)<1 and not c(b(a)/b(j)):return j

Returns a binary string without separator. As @AndersKaseorg points out, there are inputs for which @fəˈnɛtɪk's solution doesn't work because the division represents a negative coefficient which is disallowed. To work around this, I use a very large base and test that there is no borrow in the division.

Neil

Posted 2017-03-30T10:10:01.960

Reputation: 95 035

Okay, I think this is an actual counterexample: f([1, 1, 1, 0, 0, 0, 1, 1, 1], 3) incorrectly returns 101. – Anders Kaseorg – 2017-06-02T19:26:16.700

@AndersKaseorg Hmm, does reversing the order of the loop help, or is the algorithm still fundamentally broken? – Neil – 2017-06-02T19:44:00.790

I think it’s fundamentally broken without additional checks. The reverse variant fails on f([1, 0, 2, 0, 2, 0, 1], 3), and both the forward and reverse variants fail on f([1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0], 5). – Anders Kaseorg – 2017-06-02T20:36:35.893

And even if you check that i is odd, both the forward and reverse variants fail on f([1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0]*10, 5). – Anders Kaseorg – 2017-06-02T20:44:41.943

1@AndersKaseorg Ah yes, when gcd(k,n)=1, (x^kn-1)/(x^k-1) always has (x^n-1)/(x-1) as a factor, which fools @fəˈnɛtɪk's solution in any base. – Neil – 2017-06-03T15:44:21.050

@AndersKaseorg I have tweaked my answer so it's a bit more resilient now. – Neil – 2017-06-04T09:41:24.300

Okay. It still fails for cases with N = 1, though, such as the first test case given in the challenge. – Anders Kaseorg – 2017-06-04T11:06:12.373

@AndersKaseorg Ah, that seems to be down to a useless +1 which didn't achieve anything but broke the N=1 case. – Neil – 2017-06-04T11:17:34.553

Still fails when N = 1 and A has length 1. – Anders Kaseorg – 2017-06-04T11:42:15.320

@AndersKaseorg Ugh, those edge cases where my base just wasn't large enough... – Neil – 2017-06-04T11:51:06.577

This version looks correct now. – Anders Kaseorg – 2017-06-04T20:35:14.463

You can combine the two assignments into a single line, separated by a semicolon, to save a byte. – Esolanging Fruit – 2017-06-04T22:40:40.317

@Challenger5 Thanks, but I'm not going to get anywhere near AndersKaseorg's far superior answer, so it's hardly worth it. – Neil – 2017-06-04T23:13:35.537

1

Pari/GP, 77 74 96 80 bytes

n->a->[v|d<-divisors(b=Pol(a)),(v=Vec(d))%2==v&&vecmin(Vec(b/d))>=0&&d%x&&#d==n]

Returns all solutions.

First converts the array a to a polynomial b. Then chooses from the divisors b the polynomials d such that the coefficients of d are all 1 and 0, and the coefficients of b / d are all nonnegative, and d(0) = 1, and deg(d) = n + 1. Finally, converts them back to arrays.

Try it online!

alephalpha

Posted 2017-03-30T10:10:01.960

Reputation: 23 988

1

Pyth, 32 bytes

f!|%FKiRJysQ,QT>#sQj/FKJ+L1^U2tE

Try it online

How it works

                           ^U2tE   Cartesian power [0, 1]^(N - 1)
                        +L1        prepend 1 to every list
f                                  filter for lists T such that:
          sQ                         sum(A)
         y                           double
        J                            assign to J
      iR    ,QT                      convert [A, T] from base J
     K                               assign to K
   %F                                fold modulo
  |                                  logical OR with
                    /FK                fold integer division over K
                   j   J               convert to base J
               >#sQ                    filter for digits greater than sum(A)
 !                                   logical NOT

The strategy is similar to my Python answer, except that since Pyth has builtins for base conversion, we can use a more efficient base k = 2 ⋅ sum(A), and check directly that every digit of the quotient is at most sum(A).

Anders Kaseorg

Posted 2017-03-30T10:10:01.960

Reputation: 29 242