Determine sequential numbers in a list (Python) - python

I'm trying to find the most pythonic way to find out if numbers in a list are sequential. To give some background, I have a list of numbers gathered that exist in a folder, and I need to find out which numbers are missing.
I gather all of the numbers, and then make another list from the range(beginning, end+1) of what numbers should be there. I very easily made something to show me all of the numbers missing:
missing = [x for x in existingNumbers if x not in shouldBeNumbers]
The problem is that if I print out all of those, there are a lot of numbers that could be condensed (i.e. 1, 2, 3, 4, 7, 10 could be printed as 1-4, 7, 10) because there could be massive amounts of numbers missing.
I've tried two approaches:
For both ways, frameRange is range(startFrame, endFrame+1) and frameList is a list generated from what exists currently.
1)
for x in frameRange:
if x not in frameList:
if originalFrame == None:
originalFrame = x
elif originalFrame:
if lastFrame == None:
lastFrame = x
elif lastFrame:
if lastFrame == x-1:
lastFrame = x
else:
if originalFrame != lastFrame:
missingFrames.append(str(originalFrame)+"-"+str(lastFrame))
originalFrame = x
lastFrame = x
else:
missingFrames.append(str(originalFrame))
originalFrame = x
lastFrame = x
if x == endFrame:
if originalFrame != lastFrame:
missingFrames.append(str(originalFrame)+"-"+str(lastFrame))
originalFrame = x
lastFrame = x
else:
missingFrames.append(str(originalFrame))
originalFrame = x
lastFrame = x
2)
i = 0
while i < len(frameRange):
if frameRange[i] in frameList:
i += 1
else:
if i + 1 < len(frameRange):
if frameRange[i + 1] in frameList:
missingFrames.append(str(frameRange[i]))
i += 1
else:
j = 1
while frameRange[i+j] not in frameList:
aheadFrameNumber = int(str(j))
if i + j + 1 < len(frameRange):
j += 1
else:
break
missingFrames.append(str(frameRange[i])+"-"+str(frameRange[aheadFrameNumber]))
if i + aheadFrameNumber + 1 < len(frameRange):
i += aheadFrameNumber + 1
else:
missingFrames.append(str(frameRange[i]))
The first way was working, but since it happens on the current frame checking the last, whenever the last frame was gone it wouldn't append the last missing section to the list. For the second way I had to keep wrapping everything in if statements because I kept getting index exceptions when moving forwards.
I think I have to step back, re-think, and approach it differently. I'm wondering if there is a much better way to do this in python that I haven't thought about yet because I don't know the function. Both ways started to get a little out of hand.

Try something like this
missing=[]
numbers.insert(0, 0) # add the minimum value on begining of the list
numbers.append(41) # add the maximum value at the end of the list
for rank in xrange(0, len(numbers)-1):
if numbers[rank+1] - numbers[rank] > 2:
missing.append("%s-%s"%(numbers[rank] +1 , numbers[rank+1] - 1))
elif numbers[rank+1] - numbers[rank] == 2:
missing.append(str(numbers[rank]+1))
print missing
which for numbers = [1,4,6,10, 12,] and numbers should be present are from 1 to 40 you will have :
['2-3', '5', '7-9', '11', '13-40']

def find_missing_range(my_numbers, range_min, range_max):
expected_range = set(range(range_min, range_max + 1))
return expected_range - set(my_numbers)
def numbers_as_ranges(numbers):
ranges = []
for number in numbers:
if ranges and number == (ranges[-1][-1] + 1):
ranges[-1] = (ranges[-1][0], number)
else:
ranges.append((number, number))
return ranges
def format_ranges(ranges):
range_iter = (("%d" % r[0] if r[0] == r[1] else "%d-%d" % r) for r in ranges)
return "(" + ", ".join(range_iter) + ")"
def main(my_numbers, range_min, range_max):
numbers_missing = find_missing_range(my_numbers, range_min, range_max)
ranges = numbers_as_ranges(numbers_missing)
return format_ranges(ranges)
if __name__ == '__main__':
range_min, range_max = 1, 40
print main([1, 4, 6, 10, 12], range_min, range_max)
print main([1, 2, 3, 4, 10, 20], range_min, range_max)
(2-3, 5, 7-9, 11, 13-40)
(5-9, 11-19, 21-40)

Related

I am merging to list with ascending order

If either i or j reach the end of their list range, how i copy the remainder of the other
list to the merge list
https://ibb.co/m9JzBYp
go to link if not get the question
def list_merge (x, y):
merge = []
i = 0
j = 0
total = len (x) + len(y)
while != total :
if x[i] < y[j]:
merge.append(x[i])
i += 1
if i >= len (x):
#how i copy the reminder
else :
merge.append(y[j])
j += 1
if j >= len (y):
#how i copy the reminder
return merge
EDIT - OP wanted the code in some specific way.. Please see second snippet.
Don't try to complicate your code.. just go with how you would do it manually and write the code.
def list_merge (x, y):
merged_list = []
i = 0
j = 0
# when one of them is empty, break out
while i < len(x) and j < len(y):
if x[i] <= y[j]:
merged_list.append(x[i])
i +=1
else:
merged_list.append(y[j])
j +=1
# if you are here, that means either one of the list is done
# so check the bounds of both lists and append the one which is not traversed
# x has some elements to add
# check how extend works... makes your code clean
if i != len(x):
merged_list.extend(x[i:])
else:
merged_list.extend(y[j:])
return merged_list
a = [1,3,5,7,10]
b = [2,4,6,8,100]
print(list_merge(a,b))
Output
[1, 2, 3, 4, 5, 6, 7, 8, 10, 100]
What OP needed
def list_merge (x, y):
merge = []
i = 0
j = 0
total = len (x) + len(y)
while len(merge) != total :
if x[i] < y[j]:
merge.append(x[i])
i += 1
if i >= len (x):
merge.extend(y[j:])
else:
merge.append(y[j])
j += 1
if j >= len (y):
merge.extend(x[i:])
return merge
a quite simple form:
def merge(*lists_in):
list_in = []
for l in lists_in:
list_in += l
i = 0
while True:
if i == list_in.__len__() -1:
break
if list_in[i] > list_in[i+1]:
temp = list_in[i]
list_in[i] = list_in[i+1]
list_in[i+1] = temp
i = 0
else:
i += 1
return list_in
Testing it:
list1 = [1,4]
list2 = [1,5,6]
list3 = [3,7,9,10]
print(merge(list1, list2, list3))
Out:
[1, 1, 3, 4, 5, 6, 7, 9, 10]
This can be solved rather nicely using deques, which allow you to efficiently look at and remove the first element.
from collections import deque
# A generator function that interleaves two sorted deques
# into a single sorted sequence.
def merge_deques(x, y):
while x and y:
yield (x if x[0] <= y[0] else y).popleft()
# When we reach this point, one of x or y is empty,
# so one of these doesn't yield any values.
yield from x
yield from y
# Makes a list by consuming the merged sequence of two deques.
def list_merge(x, y):
return list(merge_deques(deque(x), deque(y))

Function to sum and compare two sides of an array

For example this is array = [1,2,3,4,3,2,1]
i want computer to choose i=4 and sum both left of the four and right of the four and then compare if they are equal to each other, program prints i value to the screen.
Now i write it in here and it works as long as the i value is 0 but if i want to make this search for i>0 then i gets complicated.
this is my main program:
# importing "array" for array creations
import array as arr
# Function to find sum
# of array exlcuding the
# range which has [a, b]
def sumexcludingrange(li, a, b): # I FOUND THIS FUNCTION ONLINE BUT ALSO DIDN`T WORK EITHER.
sum = 0
add = True
# loop in li
for no in li:
# if no != a then add
if no != a and add == True:
sum = sum + no
# mark when a and b are found
elif no == a:
add = False
elif no == b:
add = True
# print sum
return sum
#lis = [1, 2, 4, 5, 6]
#a = 2
#b = 5
#sumexcludingrange(arr, 0, i-1)
def my(arr):
for i in range(len(arr)):
if i == 0: #this works when array is like [1,0,0,1] and i equals zero
sum_left = arr[0]
sum_right = sum(arr) - arr[0]
while sum_left == sum_right:
print(i)
break
elif i > 0 : # i 1 2 3 4 5 6 7 8 9..
#sumleft and sumright are not right.. what is the issue here?
sum_left = sumexcludingrange(arr, 0, i-1) #result is 16, sumexcludingrange function didn`t
#work.
print (sum_left)
#print(i)
sum_right == sum(arr) - sum_left
#print(sum_right)
#break
while sum_left == sum_right:
print(sum_left)
print(sum_right)
print("they are equal now")
break
else:
print("index cannot be a negative number.")
something = arr.array('i', [1,2,3,4,3,2,1]) # len(arr) = 7
my(something)
After seeing that i need another function to compare two sides of the array, i created a new file and write it in here and somehow it outputs this:
1
13
13
2
10
10
#array[i] array[i+1] .. + array[length-1]
array = [1,2,3,4,5,6,7] # 7 element so length=7,
for i in range(len(array)):
if i > 0 :
ln = len(array) + 1
sum_right = sum(array[i+1:ln])
sum_left = sum(array) - sum_right
print(i)
print(sum_right)
print(sum_right)
the way i think is this:
# array[i] i>0 i={1,2,3,4..}
sum_total = sum(arr) = array[0] + ..+ array[i-1] + array[i] + array[i+1] + ... + array[length-1]
sum_right = array[i+1] + .. + array[length-1]
sum_left = sum_total - sum_right
Is there a better way to accomplish that?
If I understand your question correctly, the solution could be something like this:
array = [1,2,3,4,5,6,7]
a1 = array[:4] # first 4 numbers
a2 = array[-4:] # last 4 numbers
total = sum(array)
diff = total-sum(a2)

Confusing Return Statements in Python

I'm failing to understand why the return statements are not returning the sorted value ?
I'm trying to sort a list ( doesn't contain a repeated number ) where I'm trying to push the smallest number to the left, and the largest to the right, post that the list in between is split and same function is called again.
def max_sort(i):
if i == []:
return []
else:
[min_val, max_val] = [min(i), max(i)]
min_pos = i.index(min(i))
max_pos = i.index(max(i))
'''for sorting in ascending order only, min val moves to left, max val moves to right'''
if min_pos == 0 and max_pos == len(i) - 1:
i = i[1:-1]
return [min_val] + [max_sort(i)] + [max_val]
elif min_pos == 0:
i = i[1:]
return [min_val] + max_sort(i)
elif max_pos == len(i) - 1:
i = i[:-1]
return [max_sort(i)] + [max_val]
else:
min_pos_copy = min_pos
for x in range(0, min_pos_copy):
i[min_pos] = i[min_pos - 1]
i[min_pos - 1] = min_val
min_pos = i.index(min(i))
max_pos = i.index(max(i))
max_pos_copy = max_pos
for y in range(max_pos_copy, len(i)-1):
i[max_pos] = i[max_pos + 1]
i[max_pos + 1] = max_val
max_pos = i.index(max(i))
max_sort(i)
max_sort([22, 876, 4, 101, 7, 0])
It goes down to the level of splitting the correct sequence, however does not SPLICE it back up. Why ?
You have two issues in the code. First since max_sort is supposed to return list you don't need to assign the return value to a new list. So instead of return [max_sort(i)] + [max_val] just use return max_sort(i) + [max_val]. There are couple such cases in your code.
The other problem is that else branch doesn't return anything. This can be easily fixed by changing the last line in the function to return max_sort(i). With these changes your code should work as expected.

Collatz conjecture sequence

The Collatz conjecture
what i am trying to do:
Write a function called collatz_sequence that takes a starting integer and returns the sequence of integers, including the starting point, for that number. Return the sequence in the form of a list. Create your function so that if the user inputs any integer less than 1, it returns the empty list [].
background on collatz conjecture:
Take any natural number n. If n is even, divide it by 2 to get n / 2, if n is odd multiply it by 3 and add 1 to obtain 3n + 1. Repeat the process indefinitely. The conjecture is that no matter what number you start with, you will always eventually reach 1.
What I have so far:
def collatz_sequence(x):
seq = [x]
if x < 1:
return []
while x > 1:
if x % 2 == 0:
x= x/2
else:
x= 3*x+1
return seq
When I run this with a number less than 1 i get the empty set which is right. But when i run it with a number above 1 I only get that number i.e. collatz_sequence(6) returns [6]. I need this to return the whole sequence of numbers so 6 should return 6,3,10,5,16,8,4,2,1 in a list.
You forgot to append the x values to the seq list:
def collatz_sequence(x):
seq = [x]
if x < 1:
return []
while x > 1:
if x % 2 == 0:
x = x / 2
else:
x = 3 * x + 1
seq.append(x) # Added line
return seq
Verification:
~/tmp$ python collatz.py
[6, 3, 10, 5, 16, 8, 4, 2, 1]
def collatz_sequence(x):
seq = [x]
while seq[-1] > 1:
if x % 2 == 0:
seq.append(x/2)
else:
seq.append(3*x+1)
x = seq[-1]
return seq
Here's some code that produces what you're looking for. The check for 1 is built into while statement, and it iteratively appends to the list seq.
>>> collatz_sequence(6)
[6, 3, 10, 5, 16, 8, 4, 2, 1]
Note, this is going to be very slow for large lists of numbers. A cache won't solve the speed issue, and you won't be able to use this in a brute-force solution of the project euler problem, it will take forever (as it does every calculation, every single iteration.)
Here's another way of doing it:
while True:
x=int(input('ENTER NO.:'))
print ('----------------')
while x>0:
if x%2==0:
x = x/2
elif x>1:
x = 3*x + 1
else:
break
print (x)
This will ask the user for a number again and again to be put in it until he quits
def collatz(x):
while x !=1:
print(int(x))
if x%2 == 0:
x = x/2
else:
x = 3*x+1
this is what i propose..
seq = []
x = (int(input("Add number:")))
if (x != 1):
print ("Number can't be 1")
while x > 1:
if x % 2 == 0:
x=x/2
else:
x = 3 * x + 1
seq.append (x)
print seq
This gives all the steps of a single number. It has worked with a 50-digit number in 0,3 second.
collatz = []
def collatz_sequence(x):
while x != 1:
if x % 2 == 0:
x /= 2
else:
x = (3*x + 1)/2
collatz.append(int(x))
print(collatz)
collatz_sequence()
Recursion:
def collatz(n):
if n == 1: return [n]
elif n % 2 == 0: return [n] + collatz(int(n/2))
else: return [n] + collatz(n*3+1)
print(collatz(27))
steps=0
c0 = int(input("enter the value of c0="))
while c0>1:
if c0 % 2 ==0 :
c0 = c0/2
print(int(c0))
steps +=1
else:
c0 = (3 * c0) + 1
print(int(c0))
steps +=1
print("steps= ", steps)
import numpy as np
from matplotlib.pyplot import step, xlim, ylim, show
def collatz_sequence(N):
seq = [N]
m = 0
maxN = 0
while seq[-1] > 1:
if N % 2 == 0:
k = N//2
seq.append(N//2)
if k > maxN:
maxN = k
else:
k = 3*N+1
seq.append(3*N+1)
if k > maxN:
maxN = k
N = seq[-1]
m = m + 1
print(seq)
x = np.arange(0, m+1)
y = np.array(seq)
xlim(0, m+1)
ylim(0, maxN*1.1)
step(x, y)
show()
def collatz_exec():
print('Enter an Integer')
N = int(input())
collatz_sequence(N)
This is how you can use it:
>>> from collatz_sequence import *
>>> collatz_exec()
Enter an Integer
21
[21, 64, 32, 16, 8, 4, 2, 1]
And a plot that shows the sequence:
seq = []
def collatz_sequence(x):
global seq
seq.append(x)
if x == 1:
return
if (x % 2) == 0:
collatz_sequence(x / 2)
else:
collatz_sequence((x * 3) + 1)
collatz_sequence(217)
print seq
def collataz(number):
while number > 1:
if number % 2 == 0 :
number = number //2
print(number)
elif number % 2 ==1 :
number = 3 * number + 1
print(number)
if number == 1 :
break
print('enter any number...!')
number=int(input())
collataz(number)

Converting phone number range list to prefix list

I have a phone number range, for example:
3331234-3332345
I need to write a function that converts it to list of prefixes:
3331234
...
3331239
333124
...
333129
33313
...
33319
33320
...
33322
333231
333232
333233
3332341
...
3332345
Question is not so easy. I don't need to get a list of numbers between range start and end.
My working code. It not very quick, too. Optimizations welcome.
def diap_to_prefix(a, b):
lst = ['%0*d'%(max(len(str(a)), len(str(b))), x) for x in range(int(a), int(b)+1)]
new_lst = []
while len(lst) != len(new_lst):
lst = new_lst or lst
new_lst = []
c = lst[0]
tmp_lst = [c]
for i in lst[1:]:
if c[:-1] == i[:-1]:
c = i
tmp_lst.append(c)
else:
if len(tmp_lst) == 10:
new_lst.append(c[:-1])
else:
new_lst.extend(tmp_lst)
c = i
tmp_lst = [c]
if len(tmp_lst) == 10:
new_lst.append(c[:-1])
else:
new_lst.extend(tmp_lst)
return lst
My new more optimal solution (py3.4)
def diap_to_prefix(a, b):
def inner(aa, bb, p):
if p == 1:
if a <= aa <= b:
yield aa
return
for d in range(aa, bb + 1, p):
if a <= d and d + p - 1 <= b:
yield d // p
elif not (bb < a or aa > b):
for i in range(10):
yield from inner(d + i * p // 10, d + (i + 1) * p // 10 - 1, p // 10)
a, b = int(a), int(b)
p = 10**(max(len(str(x)) for x in (a, b)) - 1)
yield from inner(a // p * p, b // p * p + p - 1, p)
You need to get the common prefix of the values separated by "-", so:
Use .split to get these and iterate through them until you find a difference
Complete the first value with zeros (to get the least number) until you get phone_len digits and do the same for the maximum (with nines)
Then, you have a simple range of numbers
Iterate through them and convert them to strings
Here it is:
phone_len = 7
R = "33312345-3332345".split("-")
prefix = ""
for i in range(len(R[0])):
if R[0][i] == R[1][i]:
prefix += R[0][i]
else:
break
m = int(R[0]+"0"*(phone_len-len(R[0])))
M = int(R[1]+"9"*(phone_len-len(R[0])))
phones = [str(n) for n in range(m, M+1)]
Here's a sketch of one way to handle this problem. I've used ellipses to mark the spots where you'll need to fill in the details explained in the comments. I'd write a function to derive the initial value of 'maxpower', everything else is simple enough to be written inline.
firstnumber = 3331234
lastnumber = 3332345
current = firstnumber
while current <= lastnumber:
# Find the largest power of 10 that exactly divides 'current'.
# Call this value 'maxpower'. 'maxpower' is a candidate for the
# size of the block of numbers that will be represented by the
# next output value.
maxpower = ... # 1, 10, 100, 1000, 10000, and so on
# If a block of size 'maxpower' would take us past the
# 'lastnumber', we can't use that block size. We must try a
# smaller block. Divide 'maxpower' by 10 until the block size
# becomes acceptable.
while (current + maxpower) > ... :
maxpower /= 10
# Now 'maxpower' is the largest acceptable size for the next
# block, so the desired prefix is 'current' divided by 'maxpower'.
# Emit that value, then add 'maxpower' to 'current' to get the new
# 'current' value for the next iteration.
print ...
current += maxpower
My working code. It not very quick, but working. Optimizations welcome.
def fill(root, prefix, value, parent, pkey):
if len(prefix) > 1:
if prefix[0] in root:
fill(root[prefix[0]], prefix[1:], value, root, prefix[0])
if pkey:
if len(parent[pkey]) == 10:
parent[pkey] = value
elif type(root) == type({}):
root[prefix[0]] = {}
fill(root[prefix[0]], prefix[1:], value, root, prefix[0])
if pkey:
if len(parent[pkey]) == 10:
parent[pkey] = value
elif type(root) == type({}):
root[prefix[0]] = value
if pkey:
if len(parent[pkey]) == 10:
parent[pkey] = value
return root
def compact(prefixes, current):
if not type(prefixes) == type({}):
return [current]
else:
rlist = []
for k, v in prefixes.iteritems():
rlist.extend(compact(v, current + k))
continue
return rlist
if __name__ == '__main__':
plist = {}
for x in range(4440000, 4490000):
fill(plist, str(x), 'value', plist, None)
#print plist
print compact(plist, '')

Categories