'Ordered (n choose p) binary encoding and decoding
I want to 'impose' order on (n choose p) binary and be able to encode/decode items.
Below you can see an example of p=2. For p > 2 it will need more loops. BTW this function is just illustration on ONE specific order, it can be different one just have to exhaust all combinations.
What I'm after is the encode/decode !!
It will be even better if it uses the INDEX value instead of bitarray i.e. 10100 => (3,5)
import numpy as np
#should exhaust all (n choose p) possibilities
def order(p=2,n=4) :
p = 0
for i in range(n):
for j in range(i+1,n):
v = np.zeros(n,dtype=int)
v[i]=1; v[j]=1; p+=1
print(f'pos:{p} {v}')
def encode(pos,p=2,n=4):
pass #should return val at pos
def decode(value,p=2,n=4):
pass #should return pos of val
order(n=4)
print()
order(n=5)
-----
pos:1 [1 1 0 0]
pos:2 [1 0 1 0]
pos:3 [1 0 0 1]
pos:4 [0 1 1 0]
pos:5 [0 1 0 1]
pos:6 [0 0 1 1]
pos:1 [1 1 0 0 0]
pos:2 [1 0 1 0 0]
pos:3 [1 0 0 1 0]
pos:4 [1 0 0 0 1]
pos:5 [0 1 1 0 0]
pos:6 [0 1 0 1 0]
pos:7 [0 1 0 0 1]
pos:8 [0 0 1 1 0]
pos:9 [0 0 1 0 1]
pos:10 [0 0 0 1 1]
seems legit :
In [36]: [nth_combination(range(10),3,i) for i in range(10)]
Out[36]: [(0, 1, 2), (0, 1, 3), (0, 1, 4), (0, 1, 5), (0, 1, 6), (0, 1, 7), (0, 1, 8), (0, 1, 9), (0, 2, 3), (0, 2, 4)]
In [6]: [nth_combination(range(1000),5,i) for i in range(100000,100010)]
Out[6]:
[(0, 1, 2, 108, 989),
(0, 1, 2, 108, 990),
(0, 1, 2, 108, 991),
(0, 1, 2, 108, 992),
(0, 1, 2, 108, 993),
(0, 1, 2, 108, 994),
(0, 1, 2, 108, 995),
(0, 1, 2, 108, 996),
(0, 1, 2, 108, 997),
(0, 1, 2, 108, 998)]
In [7]: combination_index((0,1,2,108,990),range(1000))
Out[7]: 100001
Solution 1:[1]
Below is a recursive implementation of encoding / decoding. I want to issue two caveats:
If you were to encode/decode outrageously large values, you could run into RecursionDepthExceeded exceptions, as with most recursion. However, this will require insanely large input values.
Secondly, this can be a lot slower than it needs to be, due to the list concatenation (used for simplicity). If performance is a concern, you should pass an accumulator, and append to the list rather than concatenating + forming new lists constantly. At that point, probably convert the recursion into an iterative loop as well.
from math import comb
def encode(pos, n, p):
if p == n:
return [1] * n
if p == 0:
return [0] * n
if pos < comb(n-1, p-1):
return [1] + encode(pos, n-1, p-1)
else:
return [0] + encode(pos - comb(n-1, p-1), n-1, p)
def decode(value, n, p):
if all(value):
return 0
if not any(value):
return 0
head, *tail = value
if head == 1:
return decode(tail, n-1, p-1)
else:
return comb(n-1, p-1) + decode(tail, n-1, p)
Example:
for i in range(6):
e = encode(i, 4, 2)
d = decode(e, 4, 2)
print(i, e, i == d)
Output:
0 [1, 1, 0, 0] True
1 [1, 0, 1, 0] True
2 [1, 0, 0, 1] True
3 [0, 1, 1, 0] True
4 [0, 1, 0, 1] True
5 [0, 0, 1, 1] True
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Dillon Davis |
