How to increment values in a list within specific range
Question:
I have a list l =[253, 59, 2, 0, 0, 0, 0, 0]
and I want to set a range of (0, 255)
for each element in the list. I want to implement it in python. I want to increment the elements one by one such that when it reaches the max range the element should reset to 0 and the next element should increment by 1
The output should look like this after every iteration:
l =[253, 59, 2, 0, 0, 0, 0, 0]
l =[254, 59, 2, 0, 0, 0, 0, 0]
l =[255, 59, 2, 0, 0, 0, 0, 0]
l =[0, 60, 2, 0, 0, 0, 0, 0]
l =[1, 60, 2, 0, 0, 0, 0, 0]
.
.
.
.
l =[255, 60, 2, 0, 0, 0, 0, 0]
l =[0, 61, 2, 0, 0, 0, 0, 0]
Answers:
One option in pure python:
l = [253, 59, 2, 0, 0, 0, 0, 0]
pos = 0
n_iter = 10
step = 0
for step in range(n_iter):
for pos in range(len(l)):
if l[pos] < 255:
l[pos] += 1
break
else:
l[pos] = 0
print(f'step {step+1}: {l}')
Output:
step 1: [254, 59, 2, 0, 0, 0, 0, 0]
step 2: [255, 59, 2, 0, 0, 0, 0, 0]
step 3: [0, 60, 2, 0, 0, 0, 0, 0]
step 4: [1, 60, 2, 0, 0, 0, 0, 0]
step 5: [2, 60, 2, 0, 0, 0, 0, 0]
step 6: [3, 60, 2, 0, 0, 0, 0, 0]
step 7: [4, 60, 2, 0, 0, 0, 0, 0]
step 8: [5, 60, 2, 0, 0, 0, 0, 0]
step 9: [6, 60, 2, 0, 0, 0, 0, 0]
step 10: [7, 60, 2, 0, 0, 0, 0, 0]
with loop,
array = [253, 59, 2, 0, 0, 0, 0, 0]
index = 0
while index < len(array):
while array[index]<256:
array[index] += 1
print(array)
array[index] = 0
index += 1
One way with a loop:
def inc_list(l):
for i in range(len(l)):
l[i] += 1
if l[i] > 255:
l[i] = 0
else:
return
for _ in range(300):
print(l)
inc_list(l)
Alternatively, a recursive solution:
def inc_list(l):
if not l:
return []
elif l[0] == 255:
return [0] + inc_list(l[1:])
else:
return [l[0] + 1] + l[1:]
for _ in range(300):
print(l)
l = inc_list(l)
tl;dr: one-liner is
import struct
list(struct.pack('<Q', struct.unpack('<Q', bytes(start))[0]+1))
1- Counting in 64 bits
Just to provide another view, this what you seem to do is counting in a list of 8 bytes representing a 64 bits integer, little-endian… Why don’t you just do that?
def plusOne(l):
n=0
b=1
for dg in l:
n+=b*dg
b*=256
n+=1
res=[]
for i in range(len(l)):
res.append(n%256)
n//=256
return res
As is, this answer is not faster than the others. It is even way slower, I surmise (haven’t timeit it. But, well, it is obviously more complex).
But if what you want to have is just a counter, then you could simplify by keeping just an integer as state value, and generate the list
2- Iterator
def listCounter(start, size):
while True:
res=[]
x=start
for i in range(size):
res.append(x%256)
x//=256
yield res
start+=1
myiter=listCounter(254, 8)
print(next(myiter))
print(next(myiter))
for _,l in zip(range(20),listCounter(510,8)):
print(l)
3- Struct (using internal representation)
Since that is how 8 bytes integer are counted on the hardware, you can even rely on the cpu to do that, rather than trying to imitate it in python.
import struct
l=[254, 255, 0, 0, 1, 0, 0, 0]
nextl=list(struct.pack('<Q', struct.unpack('<Q', bytes(l))[0]+1))
Or in the for of an iterator
import struct
def listCounter(start):
while True:
yield start
start=list(struct.pack('<Q', struct.unpack('<Q', bytes(start))[0]+1))
for _,l in zip(range(10), listCounter([254,255,0,0,1,0,0,0])):
print(l)
[254, 255, 0, 0, 1, 0, 0, 0]
[255, 255, 0, 0, 1, 0, 0, 0]
[0, 0, 1, 0, 1, 0, 0, 0]
[1, 0, 1, 0, 1, 0, 0, 0]
[2, 0, 1, 0, 1, 0, 0, 0]
[3, 0, 1, 0, 1, 0, 0, 0]
[4, 0, 1, 0, 1, 0, 0, 0]
[5, 0, 1, 0, 1, 0, 0, 0]
[6, 0, 1, 0, 1, 0, 0, 0]
[7, 0, 1, 0, 1, 0, 0, 0]
Explanation
bytes(l)
is a byte array containing the 8 bytes in l
bytes(l) -> b'xfexffx00x00x01x00x00x00'
Those 8 bytes are the 64 bits representation of a number. Which we can extract by struct.unpack
, specifying the format <Q
(64 bits integer, little-endian, that is increasing first bytes first)
struct.unpack('<Q', bytes(l)) → (4295032830,)
It is in form of tuple because we could have asked for a succession of different numbers. So
struct.unpack('<Q', bytes(l))[0] → 4295032830
We can increment it
struct.unpack('<Q', bytes(l))[0]+1 → 4295032831
And we can get the 64 bits bytes array representation of this new integer, using pack
struct.pack('<Q',struct.unpack('<Q', bytes(l))[0]+1) → b'xffxffx00x00x01x00x00x00'
This bytes array are just numbers between 0 and 255, we can easily get them converting it to a list
list(struct.pack('<Q',struct.unpack('<Q', bytes(l))[0]+1)) → [255, 255, 0, 0, 1, 0, 0, 0]
The problem is equivalent to finding representations of consecutive integers in positional numeral system with base 256 (but you could use mixed bases as well).
numpy
ravel and unravel
For smaller lists you could find integer that has a form l = [253, 59, 2, 0]
value = np.ravel_multi_index(l, [256] * 4, order='F') #sets value to 146429
And then use np.unravel_index to get back representations of first, say, 5 integers:
np.transpose(np.unravel_index(np.arange(value, value+5), [256]*4, order='F'))
>>> array([[253, 59, 2, 0],
[254, 59, 2, 0],
[255, 59, 2, 0],
[ 0, 60, 2, 0],
[ 1, 60, 2, 0]], dtype=int64)
Larger cases
In your case dimension = [256] * 8
is required. This is too large because integers can’t exceed 2**63
in numpy
and ValueError
would be thrown. In this case you could implement your own way that replaces np.ravel_multi_index
and np.unravel_index
:
shape = np.cumprod([1, 256, 256, 256, 256, 256, 256, 256], dtype=np.int64)
value = np.dot(l, shape) #same 146429
def unravel(values, shape=[256] * 8):
steps = len(values)
result_arr = np.empty(shape=(len(shape), steps), dtype=int)
for j in range(len(shape)):
result_arr[j] = values % shape[j]
values = values // shape[j]
return np.transpose(result_arr)
unravel(np.arange(value, value+5))
>>> array([[253, 59, 2, 0, 0, 0, 0, 0],
[254, 59, 2, 0, 0, 0, 0, 0],
[255, 59, 2, 0, 0, 0, 0, 0],
[ 0, 60, 2, 0, 0, 0, 0, 0],
[ 1, 60, 2, 0, 0, 0, 0, 0]])
Note that only 8 iterations are required in all cases so it should be faster than Python – only ways.
I have a list l =[253, 59, 2, 0, 0, 0, 0, 0]
and I want to set a range of (0, 255)
for each element in the list. I want to implement it in python. I want to increment the elements one by one such that when it reaches the max range the element should reset to 0 and the next element should increment by 1
The output should look like this after every iteration:
l =[253, 59, 2, 0, 0, 0, 0, 0]
l =[254, 59, 2, 0, 0, 0, 0, 0]
l =[255, 59, 2, 0, 0, 0, 0, 0]
l =[0, 60, 2, 0, 0, 0, 0, 0]
l =[1, 60, 2, 0, 0, 0, 0, 0]
.
.
.
.
l =[255, 60, 2, 0, 0, 0, 0, 0]
l =[0, 61, 2, 0, 0, 0, 0, 0]
One option in pure python:
l = [253, 59, 2, 0, 0, 0, 0, 0]
pos = 0
n_iter = 10
step = 0
for step in range(n_iter):
for pos in range(len(l)):
if l[pos] < 255:
l[pos] += 1
break
else:
l[pos] = 0
print(f'step {step+1}: {l}')
Output:
step 1: [254, 59, 2, 0, 0, 0, 0, 0]
step 2: [255, 59, 2, 0, 0, 0, 0, 0]
step 3: [0, 60, 2, 0, 0, 0, 0, 0]
step 4: [1, 60, 2, 0, 0, 0, 0, 0]
step 5: [2, 60, 2, 0, 0, 0, 0, 0]
step 6: [3, 60, 2, 0, 0, 0, 0, 0]
step 7: [4, 60, 2, 0, 0, 0, 0, 0]
step 8: [5, 60, 2, 0, 0, 0, 0, 0]
step 9: [6, 60, 2, 0, 0, 0, 0, 0]
step 10: [7, 60, 2, 0, 0, 0, 0, 0]
with loop,
array = [253, 59, 2, 0, 0, 0, 0, 0]
index = 0
while index < len(array):
while array[index]<256:
array[index] += 1
print(array)
array[index] = 0
index += 1
One way with a loop:
def inc_list(l):
for i in range(len(l)):
l[i] += 1
if l[i] > 255:
l[i] = 0
else:
return
for _ in range(300):
print(l)
inc_list(l)
Alternatively, a recursive solution:
def inc_list(l):
if not l:
return []
elif l[0] == 255:
return [0] + inc_list(l[1:])
else:
return [l[0] + 1] + l[1:]
for _ in range(300):
print(l)
l = inc_list(l)
tl;dr: one-liner is
import struct
list(struct.pack('<Q', struct.unpack('<Q', bytes(start))[0]+1))
1- Counting in 64 bits
Just to provide another view, this what you seem to do is counting in a list of 8 bytes representing a 64 bits integer, little-endian… Why don’t you just do that?
def plusOne(l):
n=0
b=1
for dg in l:
n+=b*dg
b*=256
n+=1
res=[]
for i in range(len(l)):
res.append(n%256)
n//=256
return res
As is, this answer is not faster than the others. It is even way slower, I surmise (haven’t timeit it. But, well, it is obviously more complex).
But if what you want to have is just a counter, then you could simplify by keeping just an integer as state value, and generate the list
2- Iterator
def listCounter(start, size):
while True:
res=[]
x=start
for i in range(size):
res.append(x%256)
x//=256
yield res
start+=1
myiter=listCounter(254, 8)
print(next(myiter))
print(next(myiter))
for _,l in zip(range(20),listCounter(510,8)):
print(l)
3- Struct (using internal representation)
Since that is how 8 bytes integer are counted on the hardware, you can even rely on the cpu to do that, rather than trying to imitate it in python.
import struct
l=[254, 255, 0, 0, 1, 0, 0, 0]
nextl=list(struct.pack('<Q', struct.unpack('<Q', bytes(l))[0]+1))
Or in the for of an iterator
import struct
def listCounter(start):
while True:
yield start
start=list(struct.pack('<Q', struct.unpack('<Q', bytes(start))[0]+1))
for _,l in zip(range(10), listCounter([254,255,0,0,1,0,0,0])):
print(l)
[254, 255, 0, 0, 1, 0, 0, 0]
[255, 255, 0, 0, 1, 0, 0, 0]
[0, 0, 1, 0, 1, 0, 0, 0]
[1, 0, 1, 0, 1, 0, 0, 0]
[2, 0, 1, 0, 1, 0, 0, 0]
[3, 0, 1, 0, 1, 0, 0, 0]
[4, 0, 1, 0, 1, 0, 0, 0]
[5, 0, 1, 0, 1, 0, 0, 0]
[6, 0, 1, 0, 1, 0, 0, 0]
[7, 0, 1, 0, 1, 0, 0, 0]
Explanation
bytes(l)
is a byte array containing the 8 bytes in l
bytes(l) -> b'xfexffx00x00x01x00x00x00'
Those 8 bytes are the 64 bits representation of a number. Which we can extract by struct.unpack
, specifying the format <Q
(64 bits integer, little-endian, that is increasing first bytes first)
struct.unpack('<Q', bytes(l)) → (4295032830,)
It is in form of tuple because we could have asked for a succession of different numbers. So
struct.unpack('<Q', bytes(l))[0] → 4295032830
We can increment it
struct.unpack('<Q', bytes(l))[0]+1 → 4295032831
And we can get the 64 bits bytes array representation of this new integer, using pack
struct.pack('<Q',struct.unpack('<Q', bytes(l))[0]+1) → b'xffxffx00x00x01x00x00x00'
This bytes array are just numbers between 0 and 255, we can easily get them converting it to a list
list(struct.pack('<Q',struct.unpack('<Q', bytes(l))[0]+1)) → [255, 255, 0, 0, 1, 0, 0, 0]
The problem is equivalent to finding representations of consecutive integers in positional numeral system with base 256 (but you could use mixed bases as well).
numpy
ravel and unravel
For smaller lists you could find integer that has a form l = [253, 59, 2, 0]
value = np.ravel_multi_index(l, [256] * 4, order='F') #sets value to 146429
And then use np.unravel_index to get back representations of first, say, 5 integers:
np.transpose(np.unravel_index(np.arange(value, value+5), [256]*4, order='F'))
>>> array([[253, 59, 2, 0],
[254, 59, 2, 0],
[255, 59, 2, 0],
[ 0, 60, 2, 0],
[ 1, 60, 2, 0]], dtype=int64)
Larger cases
In your case dimension = [256] * 8
is required. This is too large because integers can’t exceed 2**63
in numpy
and ValueError
would be thrown. In this case you could implement your own way that replaces np.ravel_multi_index
and np.unravel_index
:
shape = np.cumprod([1, 256, 256, 256, 256, 256, 256, 256], dtype=np.int64)
value = np.dot(l, shape) #same 146429
def unravel(values, shape=[256] * 8):
steps = len(values)
result_arr = np.empty(shape=(len(shape), steps), dtype=int)
for j in range(len(shape)):
result_arr[j] = values % shape[j]
values = values // shape[j]
return np.transpose(result_arr)
unravel(np.arange(value, value+5))
>>> array([[253, 59, 2, 0, 0, 0, 0, 0],
[254, 59, 2, 0, 0, 0, 0, 0],
[255, 59, 2, 0, 0, 0, 0, 0],
[ 0, 60, 2, 0, 0, 0, 0, 0],
[ 1, 60, 2, 0, 0, 0, 0, 0]])
Note that only 8 iterations are required in all cases so it should be faster than Python – only ways.