Related
i want to be able to add, subtract, divide, multiply etc with integers in a list and in order.
I know you can use sum() to add, but i also want to be able to subtract, etc in order... so i tried making a for loop idk if thats the right thing to do, but it doesn't give me the right output and it really confuses me because it really seems like it should work. I was wondering if anyone knows how to fix this or explain why its not giving me the same output as i expected.
my_list = [100, 15, 3]
for i in my_list:
i -= i
print(i)
# 100 - 15 - 3 = 82
# Wanted output: 82
# Actual output: 0
my_list = [100, 15]
for i in my_list:
i += i
print(i)
# 100 + 15 = 115
# Wanted output: 115
# Actual output: 30
There are two main issues with your code:
i can't be your loop variable and the sum, because it will be overwritten all the time. So make two variables.
Your first task is different from the second. The sum is easy: take all the values of the list and add them, so the order is irrelevant. For your subtraction it's different because you have to take the first value and subtract all others, so it's basically +100-15-3, which means that also the order of the values in the list matter.
There are more elegant ways to solve it, but for the beginning this should be better to understand.
my_list = [100, 15, 3]
my_difference = my_list[0] #initialize with the first value of your list
my_list_sub = my_list[1:] #make a new list with the remaining numbers
for val in my_list_sub:
my_difference=my_difference-val
print(my_difference)
my_list = [100, 15]
my_sum = 0 #initialize your sum with 0
for val in my_list:
my_sum=my_sum+val
print(my_sum)
As others already pointed out: The "running"/temporary variable is overwritten in every loop. You can try this out with a simple test:
for entry in [0, 'a', 13.37]:
print(entry)
It's always a good idea of trying out what happens in simple cases to learn what is going on.
But your idea of solving this with a loop is absolutely fine. If you want to re-use this functionallity later, it is also nice to wrap that in a function.
Assume integer values my_values = [100, 50, 123, 51, 124, 121] in the following examples.
Lets first tacle the sum.
def calculate_sum(values: list) -> int:
result = 0
for entry in values:
result += entry
return result
Check that it does what we want with
print(calculate_sum(my_values))
print(sum(my_values))
Now difference is 'almost' like summing up, but you want to sum up all values but the first one, and then compute the difference to the first one (a-b-c-d = a-(b+c+d)). Great, that we have already a method for summing up stuff, so we could simply do
def calculate_difference(values: list) -> int:
first, *other = values
return first - calculate_sum(other)
Note the *-marker in front of the other variable. When assigning a list two multiple variables, they are "unpacked" by python. Thus a, b, c = [0, 1, 2] would assign 0 to a and so on. However, when we do a, b = [0, 1, 2], then python complains because there are too many variables to unpack in the list (3 > 2). With the asterisk we simply tell python to put all other values, not used so far, into this special variable again. a, b, *rest = [1, 2, 3, 4, 5, 6] is also possible.
Ok, computing the product is as easy as summing up, just replace += by *= in the method. And for the quotient we can do the same as for the difference, since a * 1/b * 1/c * 1/d = a / (b*c*d). However, note that if the divisor is zero, python will raise an Error DivisionByZero, as this is not legal. Also, the result of the method is float and no longer int.
I am sorry if the title is a misnomer and/or doesn't properly describe what this is all about, you are welcome to edit the title to make it clear once you understand what this is about.
The thing is very simple, but I find it hard to describe, this thing is sorta like a number system, except it is about lists of integers.
So we start with a list of integers with only zero, foreach iteration we add one to it, until a certain limit is reached, then we insert 1 at the start of the list, and set the second element to 0, then iterate over the second element until the limit is reached again, then we add 1 to the first element and set the second element 0, and when the first element reaches the limit, insert another element with value of 1 to the start of the list, and zero the two elements after it, et cetera.
And just like this, when a place reaches limit, zero the place and the places after it, increase the place before it by one, and when all available places reach limit, add 1 to the left, for example:
0
1
2
1, 0
1, 1
1, 2
2, 0
2, 1
2, 2
1, 0, 0
The limit doesn't have to be three.
This is what I currently have that does something similar to this:
array = []
for c in range(26):
for b in range(26):
for a in range(26):
array.append((c, b, a))
I don't want leading zeroes but I can remove them, but I can't figure out how to do this with a variable number of elements.
What I want is a function that takes two arguments, limit (or base) and number of tuples to be returned, and returns the first n such tuples in order.
This must be very simple, but I just can't figure it out, and Google returns completely irrelevant results, so I am asking for help here.
How can this be done? Any help will truly be appreciated!
Hmm, I was thinking about something like this, but very unfortunately I can't make it work, please help me figure out why it doesn't work and how to make it work:
array = []
numbers = [0]
for i in range(1000):
numbers[-1] += 1
while 26 in numbers:
index = numbers.index(26)
numbers[index:] = [0] * (len(numbers) - index)
if index != 0:
numbers[index - 1] += 1
else:
numbers.insert(0, 1)
array.append(numbers)
I don't quite understand it, my testing shows everything inside the loop work perfectly fine outside the loop, the results are correct, but it just simply magically will not work in a loop, I don't know the reason for this, it is very strange.
I discovered the fact that if I change the last line to print(numbers) then everything prints correctly, but if I use append only the last element will be added, how so?
from math import log
def number_to_base(n,base):
number=[]
for digit in range(int(log(n+0.500001,base)),-1,-1):
number.append(n//base**digit%base)
return number
def first_numbers_in_base(n,base):
numbers=[]
for i in range(n):
numbers.append(tuple(number_to_base(i,base)))
return numbers
#tests:
print(first_numbers_in_base(10,3))
print(number_to_base(1048,10))
print(number_to_base(int("10201122110212",3),3))
print(first_numbers_in_base(25,10))
I finally did it!
The logic is very simple, but the hard part is to figure out why it won't work in a loop, turns out I need to use .copy(), because for whatever reason, doing an in-place modification to a list directly modifies the data reside in its memory space, such behavior modifies the same memory space, and .append() method always appends the latest data in a memory space.
So here is the code:
def steps(base, num):
array = []
numbers = [0]
for i in range(num):
copy = numbers.copy()
copy[-1] += 1
while base in copy:
index = copy.index(base)
copy[index:] = [0] * (len(copy) - index)
if index != 0:
copy[index - 1] += 1
else:
copy.insert(0, 1)
array.append(copy)
numbers = copy
return array
Use it like this:
steps(26, 1000)
For the first 1000 lists in base 26.
Here is a a function, that will satisfy original requirements (returns list of tuples, first tuple represents 0) and is faster than other functions that have been posted to this thread:
def first_numbers_in_base(n,base):
if n<2:
if n:
return [(0,)]
return []
numbers=[(0,),(1,)]
base-=1
l=-1
num=[1]
for i in range(n-2):
if num[-1]==base:
num[-1]=0
for i in range(l,-1,-1):
if num[i]==base:
num[i]=0
else:
num[i]+=1
break
else:
num=[1]+num
l+=1
else:
num[-1]+=1
numbers.append(tuple(num))#replace tuple(num) with num.copy() if you want resutl to contain lists instead of tuples.
return numbers
I have data about arrays that each have different dimensions. This data looks like this:
spatial_dimensions = {
'x': 5,
'y': 2,
'z': 4
}
Another array could be described like this:
table_dimensions = {
'rows': 10,
'columns': 5
}
I also have data about what slots are taken in each array. That is expressed like this for the data pertaining to the spatial_dimensions array:
occupied_slots = [
[1,2,3],
[4,2,2],
[1,1,1]
]
For the table_dimensions array it could e.g. be
occupied_slots = [
[2,3],
[5,2],
[6,1],
[5,5]
]
I don't have the "full" arrays, only their dimensions and a list of occupied slots.
I want to randomly get an empty slot from an array and return it as a list (that list describes the location in the array).
In the above examples, a random empty slot could be [1, 2, 2] and [4, 3] respectively.
I want to do this in pure Python. I don't want to use numpy as it would introduce a dependency on my project only for this specific issue.
I'm stuck at finding a way of finding an empty slot without re-creating the whole array in memory and filtering out the occupied slots, as I'm afraid that will be too expensive. Especially since there is no limit on the array dimensions.
PS -- This is not an assignment I got; I tried to abstract away all the project details in this question.
Update
I'm currently using this code (based on How to iterate over this n-dimensional dataset?):
import itertools
import random
dimensions = {
'x': 2,
'y': 4,
'z': 3
}
occupied = [
[2,3,1],
[1,1,1]
]
loopover = [range(1, i + 1) for i in [dimensions[key] for key in dimensions.keys()]]
print(random.choice([i for i in itertools.product(*loopover) if list(i) not in occupied]))
As #ekhumoro commented, this recreates the whole array in memory before passing it to random.choice() which is indeed what I'd like to avoid.
IIUC, could you randomly pick elements and then check them against occupied_slots?
import random
occupied_slots = [
[1,2,3],
[4,2,2],
[1,1,1]
]
n_dim = 3
slots_list = occupied_slots
maxi = max(max(i) for i in slots_list)
mini = min(min(i) for i in slots_list)
empty = random.choices(range(mini, maxi+1), k=n_dim)
while empty in occupied_slots:
empty = random.choices(range(mini, maxi+1), k=n_dim)
As you point out, if you have many possibilities but few choices left, this will be slow and erratic. With 10,000 options and 1 choice left, my %%timeit had an average of 8 seconds with a large variation.
But in that specific case, it seems like just finding the set difference between all the possible slot arrays and the occupied slots might be the most straightforward.
To integrate these 2 options, you could define a function which has a tweakable threshold for when to choose one approach over the other, i.e. if the number of occupied slots is more than k of the total possibilities, then compute all the possibilities and find the set difference. Otherwise, try randomly picking numbers until you find an empty slot:
def get_empty_slot(occupied, k=.5):
maxi = max(max(i) for i in occupied)
mini = min(min(i) for i in occupied)
n_dim = len(occupied[0])
numbers = range(mini, maxi+1)
total_possible = len(numbers) ** n_dim
if len(occupied) / total_possible < k:
empty = random.choices(numbers, k=n_dim)
while empty in occupied:
empty = random.choices(numbers, k=n_dim)
else:
occupied_tuple = [tuple(i) for i in occupied]
all_combos = itertools.product(numbers, repeat=n_dim)
leftover = tuple(set(all_combos) - set(occupied_tuple))
empty = list(random.choice(leftover))
return empty
I tested this with the following; e should always be [0,0,0] as this is the only possibility:
combos = [list(i) for i in list(itertools.product(range(50), repeat=3))]
combos.remove([0,0,0])
e = get_empty_slot(combos, k=.5)
The set difference approach seems to perform fine with over 100,000 possibilities (and 1 choice left); it also performs well with much fewer possibilities. So, random element picking might not be significantly better in any case (this could be tested), and it begs the question of whether comparison against all possible combinations is really too expensive, and what an alternative would look like if so.
I'm new to Python and SWE in general so excuse the simple questions. I was given the following coding challenge by an interviewer. And I came up with the following solution. But I was passed over becuase it didnt meet their performance criteria. I was wondering if anyone could give me pointers on how I can do better on this question and in general for questions like this. I've found other answers solving the question, but I wanted specific answers to my implementation.
Here is the Feedback I recieved:
The while (zip_range_list): line sticks out: you don't see a lot of
while loops in Python, you don't have to put parentheses around the
test expression, and solving this problem with a while loop is a
weird thing to do. Why are while loops a bad idea?
Adding a range to reduced_zip_ranges before it's reduced, and then
continually referring to the element you just added as
reduced_zip_ranges[-1] instead of having a separate binding for it
reads awkwardly. Why is this awkward?
The construct range_check = range(low-1, high+2) may be correct, but it's both strange to look at and ridiculously space-wasteful: instead of comparing endpoints he builds a list of the entire range of numbers just to check
membership in that range. He builds these over and over again in a
loop within a loop. I see the point here. I was trying to avoid a
long if-statement. Wasn't a good idea.
Speaking of "loop within a loop", this is an O(N-squared) algorithm when it could have been O(N) after the sort. I guess I overlooked this, I see 0(n^2) now. How can I avoid this?
The routine has two different non-exceptional return points; the one within the loop is unnecessary (the code works as well with it commented out).
PROBLEM
Given a collection of zip code ranges (each range includes both
their upper and lower bounds),
provide an algorithm that produces the minimum number of ranges required
to represent the same coverage as the input.
Input: [[14,17], [4,7], [2,5], [10,12] , [15,16], [4,9], [11,13]]
Output: [[2,17]]
# Implementation
def zip_range_reducer(zip_range_list):
if not zip_range_list:
raise Exception("Empty list of ranges provided!")
reduced_zip_ranges = []
zip_range_list.sort()
while (zip_range_list):
no_overlap_ranges = []
reduced_zip_ranges.append(zip_range_list[0])
if len(zip_range_list) == 1:
return reduced_zip_ranges
zip_range_list.pop(0)
for zip_range in zip_range_list:
low, high = reduced_zip_ranges[-1][0], reduced_zip_ranges[-1][1]
range_check = range(low-1, high+2)
if zip_range[0] in range_check or zip_range[1] in range_check:
reduced_zip_ranges[-1][0] = min(reduced_zip_ranges[-1][0], zip_range[0])
reduced_zip_ranges[-1][1] = max(reduced_zip_ranges[-1][1], zip_range[1])
else:
no_overlap_ranges.append(zip_range)
zip_range_list = no_overlap_ranges
return reduced_zip_ranges
Also
Here is a O(n) solution. [Excluding the complexity of sorting]
def reduceRangeList(ranges):
# sorting the list based on the lower limit, and then based on size as tie breaker
# note we need the bigger ranges to come before the smaller ranges
ranges.sort(key= lambda pair : (pair[0], - pair[1]))
reduced= [] # new set of ranges are stored here
# we use a parent range to decide if a range is inside other ranges
parent= ranges[0]
# this will for sure be part of the solution,
# because it is the largest, leftmost range
reduced.append(ranges[0])
for x in ranges:
if parent[0] <= x[0] and x[1] <= parent[1]:
# this range is completely within another range, ignore!
continue
elif x[0] <= parent[1]:
# this range is partially inside the parent range
# so we set the parent to cover this two range
parent= [parent[0], x[1]]
else:
#this range is completely outside the parent range
parent= x
# If the range is completely or partially outside other ranges...
# I'm placing it here to avoid duplicate code
reduced.append(x)
return reduced
def main():
ranges= [[1,5], [2, 4], [6, 7], [2,7], [9,10]]
# ranges= [[1,5], [1,4], [2, 6]]
# ranges= [[1,2], [3,4], [5,6], [1,6], [6,7], [4, 8]]
reduced= reduceRangeList(ranges)
print(reduced)
if __name__ == '__main__':
main()
I'm looking to create a program which randomly generates coins on an 8x8 grid. I've got two lists being created (one list for the X co-ordinate and list for the Y co-ordinate). On these lists, the two co-ordinates cannot be the same. It's difficult to explain, so here's what I mean by example:
[1, 7, 4, **6**, 9, 2, 3, **6**, 8, 0] (list for the x co-ordinate)
[9, 3, 3, **1**, 2, 8, 0, **1**, 6, 1] (list for the y co-ordinate)
So, two lists are created. However (6,1) appears twice. I don't want this. So, how would I allow for this in my code, to ensure that this is ignored and the numbers are regenerated into different co-ordinates? The code I have is below, I don't really know how to implement such a system thing!
def treasurePro():
global coinListX, coinListY
coinListX = []
coinListY = []
for x in range(10):
num = randint(0,8)
coinListX.append(num)
print(coinListX)
for x in range(10):
num = randint(0,8)
if num == 0 and coinListX[x] == 0:
treasurePro() #goes back to the beginning to restart.
else:
coinListY.append(num)
print(coinListY)
Don't create two lists with coordinates, at least not initially. That only makes it harder to detect duplicates.
You could either create tuples with coordinates so you can detect duplicates, or even produce a range of integers that represent your coordinates in sequence, then sample from those. The latter is extremely efficient.
To create tuples, essentially you want to create 8 unique such tuples:
def treasurePro():
coords = []
while len(coords) < 8:
coord = randint(0, 8), randint(0, 8)
if coord not in coords:
coords.append(coord)
# now you have 8 unique pairs. split them out
coinListX, coinListY = zip(*coords)
This isn't all that efficient, as the coord not in coords test has to scan the whole list which is growing with each new coordinate. For a large number of coordinates to pick, this can slow down significantly. You'd have to add an extra seen = set() object that you also add coordinates to and test again in the loop to remedy that. There is a better way however.
Your board is a 9x9 size, so you have 81 unique coordinates. If you used random.sample() on a range() object (xrange() in Python 2), you could trivially create 8 unique values, then 'extract' a row and column number from those:
def treasurePro():
coords = random.sample(range(9 * 9), 8) # use xrange in Python 2
coinListX = [c // 9 for c in coords]
coinListY = [c % 9 for c in coords]
Here random.sample() guarantees that you get 8 unique coordinates.
This is also far more efficient than generating all possible tuples up-front; using range() in Python 3 makes the above use O(K) memory, where K is the number of values you need to generate, while creating all coordinates up front would take O(N^2) memory (where N is the size of a board side).
You may want to store a list of (x, y) coordinates still rather than use two separate lists. Create one with coords = [(c // 9, c % 9) for c in coords].
Your board is small enough that you can simply generate all possibilities, take a sample, and then transpose into the desired separate lists for X and Y.
possibilities = [(a,b) for a in range(10) for b in range(10)]
places = random.sample(possibilities, 10)
x,y = zip(*places)
You want to generate random coordinates, but you also want to reject any
pair of coordinates that already appears in the list. (Incidentally,
instead of two separate lists of integers, I would suggest using one
list of ordered pairs, i.e., tuples of two integers.)
One way to reject duplicates would be to search the existing list for
the new set. This is O(n) and slower than it needs to be, though it
would certainly work in your use case where n can't exceed 64.
Another way would be to maintain a second data structure where you can
look up each of the 64 cells in O(1) time, such as an 8x8 array of
booleans. Indeed, you could use this one structure by itself; to get a
list of the coordinates used, just traverse it.
cordX = [x for x in range(10)]
cordY = cordX[:]
random.shuffle(cordX)
random.shuffle(cordY)