Exploring the xorspace

14

2

The xorspace of a set of integers is the set of all integers that can be obtained by combining the starting integers with the usual bitwise xor operator (^). For example, the xorspace of (8, 4) is (0, 4, 8, 12): 0 is 4^4, 12 is 4^8, and no other numbers can be reached. Note that the starting numbers are always included, by this definition (for example, 4 is 4^4^4).

Your goal is to write the shortest program that takes a list of non-negative integers as input, and outputs the number of elements in their xorspace.

  • Standard loopholes are forbidden.
  • Input and output can be in any of the usual formats. Input is guaranteed to be valid, non-empty, and without duplicates.
  • Your code should be able to process all test cases in less than a day.

Test cases

Input: 0
Output: 1

Input: 6
Output: 2

Input: 8 4
Ouput: 4

Input: 0 256
Output: 2

Input: 256 259 3
Output: 4

Input: 60 62 94 101 115
Output: 32

Input: 60 62 94 101 115 40 91
Output: 32

Input: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
Output: 64

Input: 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384
Output: 32768

Grimmy

Posted 2017-06-05T13:09:49.937

Reputation: 12 521

Answers

2

Pyth, 8 bytes

lu{+xM*Q

Test suite

Explanation:

To generate the xorspace, we find the fixpoint of taking the xor of every pair of numbers, adding in every number, and deduplicating. Then, we take the length of the result. This runs in 20 seconds (offline only) on the final test case.

lu{+xM*Q
lu{+xM*QGGQ    Implicit variable introduction
 u        Q    Find the fixed point of the following, starting with the input,
               where the current value is G.
      *QG      Form the Cartesian product of Q (input) and G (current)
    xM         Take the xor of every pair
   +           Add the current values
  {            Deduplicate
l              Output the length of the result.

Packed Pyth, 7 bytes

Hexdump:

0000000: d9d7 dabf 1355 51                        .....UQ

The same as the above, with a 7-bit ASCII encoding.

Put the above in a file with xxd -r, and run it as follows:

py packed-pyth.py xorspace.ppyth '[256, 259, 3]'

isaacg

Posted 2017-06-05T13:09:49.937

Reputation: 39 268

I think you can do l{mxFdy. – xnor – 2017-06-06T02:28:29.340

@xnor y applied to the 1 through 63 test case is much too slow. I don't have 2^63 memory. – isaacg – 2017-06-06T02:51:43.107

10

MATL, 11 bytes

t"G!Z~Ghu]n

Try it online!

The last test case doesn't run in the online interpreter because of memory limitations, but runs offline in less than 2 seconds on a modern computer.

Explanation

For input of size n, this does the following:

  1. Initiallize result to input.
  2. Repeat n times:
    1. Apply bitwise XOR to all pair of entries from current result and input.
    2. Attach input values to the result.
    3. Deduplicate.
  3. The output is the number of elements of the final result.

Commented code.

t      % Implicit input: row vector. Duplicate
"      % For each (i.e. do as many times as the input size)
  G!   %   Push input as a column vector
  Z~   %   Bitwise XOR with broadcast, i.e. for all pairs of entries of the
       %   two arguments. The first argument is the accumulated result
       %   from the previous iteration, the second is the input vector
  G    %   Push input again
  h    %   Postpend
  u    %   Unique values. Gives a row vector
]      % End
n      % Number of entries. Implicitly display

Example

The intermediate results (steps 2.1 and 2.3) for input [256 259 3] are:

First iteration: [256 259 3] with [256 259 3]: computing all pairs of bitwise-XOR gives the matrix

  0   3 259
  3   0 256
259 256   0

Attaching [256 259 3] and deduplicating

0 3 259 256

Second iteration: current result [0 3 259 256] with [256 259 3]. After deduplicating this again gives

0 3 259 256

Third iteration: again

0 3 259 256

So the output is 4 (number of entries of result).

Luis Mendo

Posted 2017-06-05T13:09:49.937

Reputation: 87 464

Explanation please? You can't use O(2^n). – Erik the Outgolfer – 2017-06-05T13:59:41.210

I have no idea how it works, but it’s definitely not O(2^n). It actually solves the (1 2 3 … 63) test-case pretty quickly, even though it’s the worst-case for the brute-force method. – Grimmy – 2017-06-05T14:01:38.163

@EriktheOutgolfer I still don't know what n is, but if it completes the last test case in two sexonds, it's definitely not exponential. – Dennis – 2017-06-05T14:02:12.767

@Dennis I just asked for a proof, I don't dispute anything, it's just that running time is hardware-dependent, so it's not formal proof. ;) – Erik the Outgolfer – 2017-06-05T14:06:32.870

Explanation added – Luis Mendo – 2017-06-05T14:08:45.553

2How is this so quick? I've been trying to do pretty much the same in Jelly, but the first attempt got killed after 19 minutes... (Now trying with more RAM.) – Dennis – 2017-06-05T14:13:34.373

I am still a bit confused on how this works. Can you please explain what the program does for, let's say, input of 256 259 3? – user41805 – 2017-06-05T14:19:19.667

@Dennis Perhaps I got something wrong then. Are you deduplicating after each step? How many steps are you applying? I think n suffice, where n, is input size, but I0'm not sure – Luis Mendo – 2017-06-05T14:24:45.623

@KritixiLithos Done – Luis Mendo – 2017-06-05T14:30:16.367

Hm, I was applying the steps until the result no longer changes, but with 32,768 elements, that 16 gibielements in the last check. Only doing n passes would reduce that to 4 gibielements, but since Jelly is rather slow and memory-inefficient, that probably still a no-go. – Dennis – 2017-06-05T14:33:10.580

2I believe this is O(2ⁿ) worst case; it's just that in the test that exercises it, n is only 15, so the program still runs fairly quickly. – None – 2017-06-05T14:41:21.947

2

@ais523 The intermediate numbers obtained from bitwise-XOR can never get greater than the maximum number in the input, call that M. So the size of the vector of intermediate results never exceeds M, and complexity is O(M*M). The OP has said that the exact definition of n doesn't matter, so if I define n as M I can claim this to be O(n*n).

– Luis Mendo – 2017-06-05T14:53:52.093

... or is it O(M*M*M)? Well, polynomial in any case – Luis Mendo – 2017-06-06T10:44:32.593

8

Haskell, 64 bytes

f takes a list of integers and returns an integer.

import Data.Bits
f l|m<-maximum l,m>0=2*f(min<*>xor m<$>l)|0<1=1

Try it online!

This doesn't handle an empty list, for that you can but $0: instead of the space after maximum.

How it works

  • If the maximum m of the list is zero, returns 1.
  • Otherwise, xors every element with the maximum.
    • If the result is smaller than the element, the element is replaced by it.
    • This necessarily zeroes the most significant bit set anywhere in the list.
    • Then recurses on the resulting list, doubling the result of the recursion.
  • This process essentially performs Gaussian elimination (although throwing away the final rows by setting them to 0) modulo 2, on the matrix whose rows are the bit representations of the list of numbers. The set of bit representations of the "xorspace" is the vector space modulo 2 spanned by the rows of this matrix, and whose number of elements is 2 to the power of the matrix's row rank.
  • This algorithm is polynomial time, so should definitely be better than O(2^n).

Ørjan Johansen

Posted 2017-06-05T13:09:49.937

Reputation: 6 914

This is basically the algorithm I was thinking of (for beating the complexity bounds), although this is a particularly elegant way to represent it. It's nice to see it in a properly golfed answer. – None – 2017-06-06T22:40:48.713

4

Mathematica, 52 bytes

2^MatrixRank[PadLeft@IntegerDigits[#,2],Modulus->2]&

alephalpha

Posted 2017-06-05T13:09:49.937

Reputation: 23 988

Why did you delete your Pari/GP answer? It seemed to be working fine. EDIT: nevermind, it failed some test-cases actually. – Grimmy – 2017-06-05T14:22:29.357

@Grimy Why did you accept my answer? This is a code-golf, the shortest code wins. – alephalpha – 2017-06-12T10:16:04.320

Sorry, I changed the accepted answer to the 7 bytes packed Pyth one. – Grimmy – 2017-06-12T12:13:47.287

3

05AB1E, 8 bytes

vDy^ìÙ}g

Try it online!

All test-cases finish in under 1 minute on TIO.

Emigna

Posted 2017-06-05T13:09:49.937

Reputation: 50 798

This fails the last criterion: « Your code should be able to process all test cases in less than a day (no O(2**n) stuff). » – Grimmy – 2017-06-05T13:24:48.563

@Grimy: Didn't read the 2^n part :/ – Emigna – 2017-06-05T13:25:59.160

@Grimy: Now updated to finish allt test-cases in under 1 minute (and with fewer bytes used) – Emigna – 2017-06-05T15:11:59.563

Was thinking âü^Ùg until I saw you can xor more than once, nice solution. – Magic Octopus Urn – 2017-06-05T15:27:12.533

@carusocomputing: That saves a byte, but I'm not sure about the complexity. – Emigna – 2017-06-05T15:37:00.380

It's not valid as-is (my solution), but I still think Cartesian may be usable without exploding your complexity. – Magic Octopus Urn – 2017-06-05T15:39:10.267

3

Python 2, 55 bytes

r={0}
for n in input():r|={x^n for x in r}
print len(r)

Try it online!

Beats out functions:

f=lambda l,r={0}:l and f(l[1:],r|{x^l[0]for x in r})or len(r)
f=lambda l:len(reduce(lambda r,n:r|{x^n for x in r},l,{0}))

Ørjan Johansen's beautiful row elimination method is one byte shorter.

Python 2, 54 bytes

f=lambda l:1-any(l)or 2*f([min(x,x^max(l))for x in l])

Try it online!

xnor

Posted 2017-06-05T13:09:49.937

Reputation: 115 687

2

Jelly, 9 8 bytes

0œ|⁺^¥/L

Finishes all test cases in under 8 seconds on TIO, with negligible memory requirements.

Try it online!

How it works

0œ|⁺^¥/L  Main link. Argument: A (array)

0œ|       Perform multiset union with 0, prepending 0 if A doesn't contain it.
      /   Reduce A by the link to the left.
     ¥      Combine the previous two links into a dyadic chain.
            Left argument: V (array). Right argument: n (integer)
    ^           Bitwise XOR each element in V with n.
   ⁺            This quick refers to the previous link, making it a shorthand for
                the link 'œ|'. Thus, it performs multiset union on V and the array
                of bitwise XORs.
       L  Compute the length of the result.

Dennis

Posted 2017-06-05T13:09:49.937

Reputation: 196 637

1

Python, 113 bytes

def f(x):
 u,s=[0],{0}
 while u:
	a=u.pop()
	for b in x:
	 c=a^b
	 if c not in s:u+=[c]
	 s.add(c)
 return len(s)

user1502040

Posted 2017-06-05T13:09:49.937

Reputation: 2 196

It works, but I’m counting 113 bytes; did I miss something? – Grimmy – 2017-06-05T13:53:34.887

@totallyhuman that’s probably because you’re counting tabulations as 8 bytes, rather than a single byte. – Grimmy – 2017-06-05T13:59:28.160

If the first indentation is a space, next one is a tab, and the last one a tab + a space (or 2 tabs), then it's 113 bytes – daniero – 2017-06-05T14:00:02.080

@Grimy Actually each tab is 4 spaces not 8. – Erik the Outgolfer – 2017-06-05T14:00:09.230

A full program would be shorter, as it saves a handful of indentation. Also, the for loop can be condensed into a single line, as u+=[c][c in s:] is equivalent to your if statement. – Dennis – 2017-06-05T14:55:06.040