How to chunk n segments from a nestled list - python-3.x

I'm trying to chunk 100 lists from a nestled list. I have looked looked through multiple examples on Stack Overflow, but I still cannot get something working correctly.
My primary list is named data_to_insert and it contains other lists. I would like to extract (chunk) 100 lists from the primary nestled list.
How do I accomplish this?
This is my current code, which doesn't work as needed.
def divide_chunks(l, n):
for i in range(0, len(l), n):
yield l[i:i + n]
n = 100
x = list(divide_chunks(data_to_insert, 100))
Nestled list example:
data_to_insert = [['item1','item2','item3','item4','item5','item6'],
['item1','item2','item3','item4','item5','item6'],
['item1','item2','item3','item4','item5','item6'],
['item1','item2','item3','item4','item5','item6'],
['item1','item2','item3','item4','item5','item6'],
...
[thousands of others lists go here]]
Desired output is another list (sliced_data), which contains 100 lists from the nestled list (data_to_insert).
sliced_data = [['item1','item2','item3','item4','item5','item6'],
['item1','item2','item3','item4','item5','item6'],
...
[98 more lists go here]]
I need to loop through the nestled list, data_to_insert until it's empty.

You can use random to select 100 random nested lists from your given list.
This will output 3 random nested list from original list,
import random
l = [[1,2], [3,4], [1,1], [2,3], [3,5], [0,0]]
print(random.sample(l, 3))
# output,
[[3, 4], [1, 2], [2, 3]]
If you do not want a list output then replace print(random.sample(l, 3)) with print(*random.sample(l, 3)),
# output,
[1, 2] [2, 3] [1, 1]
If you just want first 100 nested lists then do,
print(l[:100])

If i did understand your question correctly, you need first to flatten your list of list then create a chunks of it. Here is an example using chain.from_iterable from itertools module along with the code you've used to create chunks:
from itertools import chain
def chunks(elm, length):
for k in range(0, len(elm), length):
yield elm[k: k + length]
my_list = [['item{}'.format(j) for j in range(7)]] * 1000
flattened = list(chain.from_iterable(my_list))
chunks = list(chunks(flattened, 100))
print(len(chunks[10]))
Output:
100

After some time-consuming research, I developed a solution that worked. The solution below loops through a list of lists and extract 100 lists.
# Verifies that the list data_to_insert isn't empty
if len(data_to_insert) > 0:
# Obtains the length of the data to insert.
# The length is the number of sublists
# contained in the main nestled list.
data_length = len(data_to_insert)
# A loop counter used in the
# data insert process.
i = 0
# The number of sublists to slice
# from the main nestled list in
# each loop.
n = 100
# This loop execute a set of statements
# as long as the condition below is true
while i < data_length:
# Increments the loop counter
if len(data_to_insert) < 100:
i += len(data_to_insert)
else:
i += 100
# Slices 100 sublists from the main nestled list.
sliced_data = data_to_insert[:n]
# Verifies that the list sliced_data isn't empty
if len(sliced_data) > 0:
# Removes 1000 sublists from the main nestled list.
data_to_insert = data_to_insert[n:]
##################################
do something with the sliced_data
##################################
# Clears the list used to store the
# sliced_data in the insertion loop.
sliced_data.clear()
gc.collect()
# Clears the list used to store the
# data elements inserted into the
# database.
data_to_insert.clear()
gc.collect()

I developed a second method to accomplish my objective, which is based on Sufiyan Ghori suggestion of using random.
if len(my_nestled_list) > 0:
# Obtains the length of the data to insert.
# The length is the number of sublists
# contained in the main nestled list.
data_length = len(my_nestled_list))
# A loop counter used in the
# data insert process.
i = 0
# The number of sublists to slice
# from the main nestled list in
# each loop.
n = 100
# This loop execute a set of statements
# as long as the condition below is true
while i < data_length:
# Increments the loop counter
if len(my_nestled_list)) < 100:
i += len(my_nestled_list))
else:
i += 100
# Uses list comprehension to randomly select 100 lists
# from the nestled list.
random_sample_of_100 = [my_nestled_list)[i] for i in sorted(random.sample(range(len(my_nestled_list))), n))]
print (random_sample_of_100)

Related

Need to fetch 1st value from the dictionary from all the preferred keys in python [duplicate]

What is an efficient way to find the most common element in a Python list?
My list items may not be hashable so can't use a dictionary.
Also in case of draws the item with the lowest index should be returned. Example:
>>> most_common(['duck', 'duck', 'goose'])
'duck'
>>> most_common(['goose', 'duck', 'duck', 'goose'])
'goose'
A simpler one-liner:
def most_common(lst):
return max(set(lst), key=lst.count)
Borrowing from here, this can be used with Python 2.7:
from collections import Counter
def Most_Common(lst):
data = Counter(lst)
return data.most_common(1)[0][0]
Works around 4-6 times faster than Alex's solutions, and is 50 times faster than the one-liner proposed by newacct.
On CPython 3.6+ (any Python 3.7+) the above will select the first seen element in case of ties. If you're running on older Python, to retrieve the element that occurs first in the list in case of ties you need to do two passes to preserve order:
# Only needed pre-3.6!
def most_common(lst):
data = Counter(lst)
return max(lst, key=data.get)
With so many solutions proposed, I'm amazed nobody's proposed what I'd consider an obvious one (for non-hashable but comparable elements) -- [itertools.groupby][1]. itertools offers fast, reusable functionality, and lets you delegate some tricky logic to well-tested standard library components. Consider for example:
import itertools
import operator
def most_common(L):
# get an iterable of (item, iterable) pairs
SL = sorted((x, i) for i, x in enumerate(L))
# print 'SL:', SL
groups = itertools.groupby(SL, key=operator.itemgetter(0))
# auxiliary function to get "quality" for an item
def _auxfun(g):
item, iterable = g
count = 0
min_index = len(L)
for _, where in iterable:
count += 1
min_index = min(min_index, where)
# print 'item %r, count %r, minind %r' % (item, count, min_index)
return count, -min_index
# pick the highest-count/earliest item
return max(groups, key=_auxfun)[0]
This could be written more concisely, of course, but I'm aiming for maximal clarity. The two print statements can be uncommented to better see the machinery in action; for example, with prints uncommented:
print most_common(['goose', 'duck', 'duck', 'goose'])
emits:
SL: [('duck', 1), ('duck', 2), ('goose', 0), ('goose', 3)]
item 'duck', count 2, minind 1
item 'goose', count 2, minind 0
goose
As you see, SL is a list of pairs, each pair an item followed by the item's index in the original list (to implement the key condition that, if the "most common" items with the same highest count are > 1, the result must be the earliest-occurring one).
groupby groups by the item only (via operator.itemgetter). The auxiliary function, called once per grouping during the max computation, receives and internally unpacks a group - a tuple with two items (item, iterable) where the iterable's items are also two-item tuples, (item, original index) [[the items of SL]].
Then the auxiliary function uses a loop to determine both the count of entries in the group's iterable, and the minimum original index; it returns those as combined "quality key", with the min index sign-changed so the max operation will consider "better" those items that occurred earlier in the original list.
This code could be much simpler if it worried a little less about big-O issues in time and space, e.g....:
def most_common(L):
groups = itertools.groupby(sorted(L))
def _auxfun((item, iterable)):
return len(list(iterable)), -L.index(item)
return max(groups, key=_auxfun)[0]
same basic idea, just expressed more simply and compactly... but, alas, an extra O(N) auxiliary space (to embody the groups' iterables to lists) and O(N squared) time (to get the L.index of every item). While premature optimization is the root of all evil in programming, deliberately picking an O(N squared) approach when an O(N log N) one is available just goes too much against the grain of scalability!-)
Finally, for those who prefer "oneliners" to clarity and performance, a bonus 1-liner version with suitably mangled names:-).
from itertools import groupby as g
def most_common_oneliner(L):
return max(g(sorted(L)), key=lambda(x, v):(len(list(v)),-L.index(x)))[0]
What you want is known in statistics as mode, and Python of course has a built-in function to do exactly that for you:
>>> from statistics import mode
>>> mode([1, 2, 2, 3, 3, 3, 3, 3, 4, 5, 6, 6, 6])
3
Note that if there is no "most common element" such as cases where the top two are tied, this will raise StatisticsError on Python
<=3.7, and on 3.8 onwards it will return the first one encountered.
Without the requirement about the lowest index, you can use collections.Counter for this:
from collections import Counter
a = [1936, 2401, 2916, 4761, 9216, 9216, 9604, 9801]
c = Counter(a)
print(c.most_common(1)) # the one most common element... 2 would mean the 2 most common
[(9216, 2)] # a set containing the element, and it's count in 'a'
If they are not hashable, you can sort them and do a single loop over the result counting the items (identical items will be next to each other). But it might be faster to make them hashable and use a dict.
def most_common(lst):
cur_length = 0
max_length = 0
cur_i = 0
max_i = 0
cur_item = None
max_item = None
for i, item in sorted(enumerate(lst), key=lambda x: x[1]):
if cur_item is None or cur_item != item:
if cur_length > max_length or (cur_length == max_length and cur_i < max_i):
max_length = cur_length
max_i = cur_i
max_item = cur_item
cur_length = 1
cur_i = i
cur_item = item
else:
cur_length += 1
if cur_length > max_length or (cur_length == max_length and cur_i < max_i):
return cur_item
return max_item
This is an O(n) solution.
mydict = {}
cnt, itm = 0, ''
for item in reversed(lst):
mydict[item] = mydict.get(item, 0) + 1
if mydict[item] >= cnt :
cnt, itm = mydict[item], item
print itm
(reversed is used to make sure that it returns the lowest index item)
Sort a copy of the list and find the longest run. You can decorate the list before sorting it with the index of each element, and then choose the run that starts with the lowest index in the case of a tie.
A one-liner:
def most_common (lst):
return max(((item, lst.count(item)) for item in set(lst)), key=lambda a: a[1])[0]
I am doing this using scipy stat module and lambda:
import scipy.stats
lst = [1,2,3,4,5,6,7,5]
most_freq_val = lambda x: scipy.stats.mode(x)[0][0]
print(most_freq_val(lst))
Result:
most_freq_val = 5
# use Decorate, Sort, Undecorate to solve the problem
def most_common(iterable):
# Make a list with tuples: (item, index)
# The index will be used later to break ties for most common item.
lst = [(x, i) for i, x in enumerate(iterable)]
lst.sort()
# lst_final will also be a list of tuples: (count, index, item)
# Sorting on this list will find us the most common item, and the index
# will break ties so the one listed first wins. Count is negative so
# largest count will have lowest value and sort first.
lst_final = []
# Get an iterator for our new list...
itr = iter(lst)
# ...and pop the first tuple off. Setup current state vars for loop.
count = 1
tup = next(itr)
x_cur, i_cur = tup
# Loop over sorted list of tuples, counting occurrences of item.
for tup in itr:
# Same item again?
if x_cur == tup[0]:
# Yes, same item; increment count
count += 1
else:
# No, new item, so write previous current item to lst_final...
t = (-count, i_cur, x_cur)
lst_final.append(t)
# ...and reset current state vars for loop.
x_cur, i_cur = tup
count = 1
# Write final item after loop ends
t = (-count, i_cur, x_cur)
lst_final.append(t)
lst_final.sort()
answer = lst_final[0][2]
return answer
print most_common(['x', 'e', 'a', 'e', 'a', 'e', 'e']) # prints 'e'
print most_common(['goose', 'duck', 'duck', 'goose']) # prints 'goose'
Simple one line solution
moc= max([(lst.count(chr),chr) for chr in set(lst)])
It will return most frequent element with its frequency.
You probably don't need this anymore, but this is what I did for a similar problem. (It looks longer than it is because of the comments.)
itemList = ['hi', 'hi', 'hello', 'bye']
counter = {}
maxItemCount = 0
for item in itemList:
try:
# Referencing this will cause a KeyError exception
# if it doesn't already exist
counter[item]
# ... meaning if we get this far it didn't happen so
# we'll increment
counter[item] += 1
except KeyError:
# If we got a KeyError we need to create the
# dictionary key
counter[item] = 1
# Keep overwriting maxItemCount with the latest number,
# if it's higher than the existing itemCount
if counter[item] > maxItemCount:
maxItemCount = counter[item]
mostPopularItem = item
print mostPopularItem
Building on Luiz's answer, but satisfying the "in case of draws the item with the lowest index should be returned" condition:
from statistics import mode, StatisticsError
def most_common(l):
try:
return mode(l)
except StatisticsError as e:
# will only return the first element if no unique mode found
if 'no unique mode' in e.args[0]:
return l[0]
# this is for "StatisticsError: no mode for empty data"
# after calling mode([])
raise
Example:
>>> most_common(['a', 'b', 'b'])
'b'
>>> most_common([1, 2])
1
>>> most_common([])
StatisticsError: no mode for empty data
ans = [1, 1, 0, 0, 1, 1]
all_ans = {ans.count(ans[i]): ans[i] for i in range(len(ans))}
print(all_ans)
all_ans={4: 1, 2: 0}
max_key = max(all_ans.keys())
4
print(all_ans[max_key])
1
#This will return the list sorted by frequency:
def orderByFrequency(list):
listUniqueValues = np.unique(list)
listQty = []
listOrderedByFrequency = []
for i in range(len(listUniqueValues)):
listQty.append(list.count(listUniqueValues[i]))
for i in range(len(listQty)):
index_bigger = np.argmax(listQty)
for j in range(listQty[index_bigger]):
listOrderedByFrequency.append(listUniqueValues[index_bigger])
listQty[index_bigger] = -1
return listOrderedByFrequency
#And this will return a list with the most frequent values in a list:
def getMostFrequentValues(list):
if (len(list) <= 1):
return list
list_most_frequent = []
list_ordered_by_frequency = orderByFrequency(list)
list_most_frequent.append(list_ordered_by_frequency[0])
frequency = list_ordered_by_frequency.count(list_ordered_by_frequency[0])
index = 0
while(index < len(list_ordered_by_frequency)):
index = index + frequency
if(index < len(list_ordered_by_frequency)):
testValue = list_ordered_by_frequency[index]
testValueFrequency = list_ordered_by_frequency.count(testValue)
if (testValueFrequency == frequency):
list_most_frequent.append(testValue)
else:
break
return list_most_frequent
#tests:
print(getMostFrequentValues([]))
print(getMostFrequentValues([1]))
print(getMostFrequentValues([1,1]))
print(getMostFrequentValues([2,1]))
print(getMostFrequentValues([2,2,1]))
print(getMostFrequentValues([1,2,1,2]))
print(getMostFrequentValues([1,2,1,2,2]))
print(getMostFrequentValues([3,2,3,5,6,3,2,2]))
print(getMostFrequentValues([1,2,2,60,50,3,3,50,3,4,50,4,4,60,60]))
Results:
[]
[1]
[1]
[1, 2]
[2]
[1, 2]
[2]
[2, 3]
[3, 4, 50, 60]
Here:
def most_common(l):
max = 0
maxitem = None
for x in set(l):
count = l.count(x)
if count > max:
max = count
maxitem = x
return maxitem
I have a vague feeling there is a method somewhere in the standard library that will give you the count of each element, but I can't find it.
This is the obvious slow solution (O(n^2)) if neither sorting nor hashing is feasible, but equality comparison (==) is available:
def most_common(items):
if not items:
raise ValueError
fitems = []
best_idx = 0
for item in items:
item_missing = True
i = 0
for fitem in fitems:
if fitem[0] == item:
fitem[1] += 1
d = fitem[1] - fitems[best_idx][1]
if d > 0 or (d == 0 and fitems[best_idx][2] > fitem[2]):
best_idx = i
item_missing = False
break
i += 1
if item_missing:
fitems.append([item, 1, i])
return items[best_idx]
But making your items hashable or sortable (as recommended by other answers) would almost always make finding the most common element faster if the length of your list (n) is large. O(n) on average with hashing, and O(n*log(n)) at worst for sorting.
>>> li = ['goose', 'duck', 'duck']
>>> def foo(li):
st = set(li)
mx = -1
for each in st:
temp = li.count(each):
if mx < temp:
mx = temp
h = each
return h
>>> foo(li)
'duck'
I needed to do this in a recent program. I'll admit it, I couldn't understand Alex's answer, so this is what I ended up with.
def mostPopular(l):
mpEl=None
mpIndex=0
mpCount=0
curEl=None
curCount=0
for i, el in sorted(enumerate(l), key=lambda x: (x[1], x[0]), reverse=True):
curCount=curCount+1 if el==curEl else 1
curEl=el
if curCount>mpCount \
or (curCount==mpCount and i<mpIndex):
mpEl=curEl
mpIndex=i
mpCount=curCount
return mpEl, mpCount, mpIndex
I timed it against Alex's solution and it's about 10-15% faster for short lists, but once you go over 100 elements or more (tested up to 200000) it's about 20% slower.
def most_frequent(List):
counter = 0
num = List[0]
for i in List:
curr_frequency = List.count(i)
if(curr_frequency> counter):
counter = curr_frequency
num = i
return num
List = [2, 1, 2, 2, 1, 3]
print(most_frequent(List))
Hi this is a very simple solution, with linear time complexity
L = ['goose', 'duck', 'duck']
def most_common(L):
current_winner = 0
max_repeated = None
for i in L:
amount_times = L.count(i)
if amount_times > current_winner:
current_winner = amount_times
max_repeated = i
return max_repeated
print(most_common(L))
"duck"
Where number, is the element in the list that repeats most of the time
numbers = [1, 3, 7, 4, 3, 0, 3, 6, 3]
max_repeat_num = max(numbers, key=numbers.count) *# which number most* frequently
max_repeat = numbers.count(max_repeat_num) *#how many times*
print(f" the number {max_repeat_num} is repeated{max_repeat} times")
def mostCommonElement(list):
count = {} // dict holder
max = 0 // keep track of the count by key
result = None // holder when count is greater than max
for i in list:
if i not in count:
count[i] = 1
else:
count[i] += 1
if count[i] > max:
max = count[i]
result = i
return result
mostCommonElement(["a","b","a","c"]) -> "a"
The most common element should be the one which is appearing more than N/2 times in the array where N being the len(array). The below technique will do it in O(n) time complexity, with just consuming O(1) auxiliary space.
from collections import Counter
def majorityElement(arr):
majority_elem = Counter(arr)
size = len(arr)
for key, val in majority_elem.items():
if val > size/2:
return key
return -1
def most_common(lst):
if max([lst.count(i)for i in lst]) == 1:
return False
else:
return max(set(lst), key=lst.count)
def popular(L):
C={}
for a in L:
C[a]=L.count(a)
for b in C.keys():
if C[b]==max(C.values()):
return b
L=[2,3,5,3,6,3,6,3,6,3,7,467,4,7,4]
print popular(L)

return unique python lists of chars ignoring order

Problem:
Consider a python list of lists that contains a sequence of chars:
[['A', 'B'],['A','B','C'],['B','A'],['C','A','B'],['D'],['D'],['Ao','B']]
The goal is to return the unique lists, regardless of order:
[['A','B'],['A','B','C'],['D'],['Ao','B']]
Attempt:
I'm able to achieve my goal using many if/else statements with try/exceptions. What would be the most pythonic (faster) way to approach this problem? Thanks!
def check_duplicates(x,list_):
for li in list_:
if compare(x,li):
return True
def compare(s, t):
t = list(t) # make a mutable copy
try:
for elem in s:
t.remove(elem)
except ValueError:
return False
return not t
vars_list = [['A', 'B'],['A','B','C'],['B','A'],['C','A','B'],['D'],['D'],['Ao','B']]
second_list = []
for i in vars_list:
if check_duplicates(i,second_list):
continue
else:
second_list.append(i)
print(i)
Assuming that the elements of the nested lists are hashable, you can isolate the unique collections by constructing a set of frozensets from the nested list:
unique_sets = {frozenset(l) for l in vars_list}
# {frozenset({'D'}),
# frozenset({'A', 'B'}),
# frozenset({'A', 'B', 'C'}),
# frozenset({'Ao', 'B'})}
If you need a list-of-lists as the output, you can obtain one trivially with [list(s) for s in unique_sets].

How to loop through a constantly updating list?

for i in range(0, len(list_stops)):
for j in range(1, len(list_stops[i])):
current = stops_from_stop(list_stops[i][j])
list_stops.extend(current)
for k in range(0, len(current)):
for m in range(0, len(current[k])):
list_stops_x.extend([current[k][m]])
if id_b in list_stops_x:
#rest of code
list_stops is a list of lists. Eg [[1,2,3], [4,5,6]]
list_stops_x is a single list of all the numbers in list_stops. Eg [1,2,3,4,5,6]. Basically used as a condition to enter the rest of the code with a return statement at the end, so the loop does not repeat.
I find that the loop ends after reaching the final index of the first version of list_stops, but I am constantly extending list_stops and want the loop to carry on into the extensions. So for example if I extend [7,8,9] to list_stops in the loop, if id_b is not found I still want it to loop through [7,8,9], but it stops at 6.
Note: This is only a section of the entire function, but I am quite sure the problem lies here. This is also for an introductory programming course, so a simple solution will do :) The full code is below
def find_path(stops, routes, stop_a, stop_b):
id_a = stop_a[0]
id_b = stop_b[0]
unused = unused_stops()
if id_b in list(unused):
return []
total_route = list()
all_stops = stops_from_stop(id_a)
list_stops_x = stops_from_stop_x(id_a)
list_stops = stops_from_stop(id_a)
for index in range(0, len(all_stops)):
if id_b in all_stops[index]:
return find_route(stops, routes, stop_a, stop_b)
for i in range(0, len(list_stops)):
for j in range(1, len(list_stops[i])):
current = stops_from_stop(list_stops[i][j])
list_stops.extend(current)
for k in range(0, len(current)):
for m in range(0, len(current[k])):
list_stops_x.extend([current[k][m]])
if id_b in list_stops_x:
stops_used_rev = [id_b]
last_route = list_stops[len(list_stops) - 1]
current_stop = last_route[0]
stops_used_rev += [current_stop]
for i in range(0, len(list_stops)):
if (current_stop in list_stops[i]) and (list_stops[i][0] == id_a):
stops_used_rev += [id_a]
break
elif current_stop in list_stops[i]:
current_stop = last_route[0]
stops_used_rev += [current_stop]
stops_used = stops_used_rev[::-1]
for index in range(0, len(stops_used) - 1):
total_route.extend(find_route(stops, routes, stops[stops_used[index]], stops[stops_used[index + 1]]))
return total_route
stops_from_stop finds the list stops accessible from the current stop and appends to another list. stops_from_stops_x does the same but extends instead
So the issue is that we use the range(0, len(list_stops)) if we instead use enumerate in the following way, BEWARE INFINITE LOOP, due too we keep adding elements to the list. So be careful, but this should give the desired result. I have changed some part of the code so I could run it.
Enumerate makes it possible to both get the item of the list (val) and the index that we are currently at.
list_stops = [[1,2,3], [4,5,6]]
list_stops_x = [1,2,3,4,5,6]
newer = [[7, 8, 9], [10, 11, 12]]
id_b = 9
for i, val in enumerate(list_stops):
print(val)
for j in range(1, len(list_stops[i])):
current = newer
list_stops.extend(current)
for k in range(0, len(current)):
for m in range(0, len(current[k])):
list_stops_x.extend([current[k][m]])
# if id_b in list_stops_x:
# print("id_b in")
# break
EDIT 1
in your code that was in the edit suggestion you have loops that look like the following:
for i in range(0, len(list_stops)):
...
These should be replace with the following to fix the issue
for i, val in enumerate(list_stops):
...
I have a difficult time of testing the code, but try replace the for loops with the type that i provided and let me know
Edit 2
If we keep adding to the list ofcourse it will loop infinitely unless we exit somewhere. You can see this example that I add elements to list a from list b and then remove the item. As you see from the output we loop eight times so we know it works, you should reflect such changes in your code
a = [1, 2, 3, 4]
b = [5, 6, 7, 8]
for idx, val in enumerate(a):
print("Loop Count: " + str(idx))
if b:
a.append(b[0])
b.remove(b[0])
Loop Count: 0
Loop Count: 1
Loop Count: 2
Loop Count: 3
Loop Count: 4
Loop Count: 5
Loop Count: 6
Loop Count: 7

How to find best solution from all brute force combinations?

I want to find the best solution from all brute force combinations of a dictionary. For the context of the problem, I need to find out the minimum number of trips needed to transport all the cows given a weight limit.
The combinations are already given to me with the helper functions get_partitions. The function returns a nested list, with each inner list represents a trip and names of cows on that trip.
Helper functions:
def partitions(set_):
if not set_:
yield []
return
for i in range(2**len(set_)//2):
parts = [set(), set()]
for item in set_:
parts[i&1].add(item)
i >>= 1
for b in partitions(parts[1]):
yield [parts[0]]+b
def get_partitions(set_):
for partition in partitions(set_):
yield [list(elt) for elt in partition]
What I tried to do was sorting all combinations by length then iterate over them with a nested loop. If the total weight exceed the limit, then I break out of the inner loop and append the next sublist to another list. The problem is that the next sublist still contain leftover lists from the partition since their total weight is below the limit.
My code:
def brute_force_cow_transport(cows, limit):
# Generate and sort all combinations by length
partitions = [item for item in get_partitions(cows)]
partitions.sort(key=len)
# Iterate over each sublist of combinations
possibles = []
for partition in partitions:
trips = []
for section in partition:
total = sum([cows.get(cow) for cow in section])
if total > limit:
break
else:
# Appending next sublists create duplicates
trips.append(section)
possibles.append(trips)
# Remove duplicates from possible solutions
best = []
for item in possibles:
if item and item not in best:
best.append(item)
return min(best)
When I run my function, it keeps returning a different result each time. I think it's because the leftover sublists I appended to the results is causing the problem but I'm not sure:
cows = {'MooMoo': 50, 'Miss Bella': 25, 'Boo': 20,
'Milkshake': 40, 'Horns': 25, 'Lotus': 40}
>>> brute_force_cow_transport(cows, limit=100)
[['Boo', 'Miss Bella', 'MooMoo']]
Correct result:
[['MooMoo', 'Horns', 'Miss Bella'], ['Milkshake', 'Lotus', 'Boo']]
If anyone can help me point out where I went wrong, that would be greatly appreciated.
Edit: Added helper functions
We can approach this as a depth-first-search problem.
def getCows(dict, limit):
best_solution = []
best_solution_score = 0
def dfs(current_cows, current_total):
nonlocal best_solution_score
nonlocal best_solution
if current_total > best_solution_score:
#replace best solution
best_solution = [tuple(sorted(current_cows))]
best_solution_score = current_total
elif current_total == best_solution_score:
#add to best solution
best_solution.append(tuple(sorted(current_cows)))
for cow, weight in dict.items():
if cow not in current_cows and current_total + weight <= limit:
#if adding next cow under limit recurse
dfs(current_cows + [cow], current_total + weight)
dfs([], 0)
return list(set(best_solution)) #remove duplicates
cows = {'MooMoo': 50, 'Miss Bella': 25, 'Boo': 20,
'Milkshake': 40, 'Horns': 25, 'Lotus': 40}
print(getCows(cows, limit=100))
>>>[('Horns', 'Miss Bella', 'MooMoo'), ('Boo', 'Lotus', 'Milkshake')]

Optimising a fibonacci sequence generator python

I am trying to create a program which creates a Fibonacci sequence up to the value of the sequence being 200. I have the basic set up down where I can compute the sequence but I wish to display it in a certain way and I have forgotten how to achieve this.
I wish to write the numbers to an array which I have defined as empty initially, compute the numbers and assign them to the array and print said array. In my code below the computation is ok but when printed to screen, the array shows the value 233 which is above 200 and not what I'm looking for. I wish to print all the values under 200 which I've stored in an array.
Is there a better way to initially define the array for what I want and what is the correct way to print the array at the end with all elements below 200?
Code follows:
#This program calculates the fibonacci sequence up to the value of 200
import numpy as np
x = np.empty(14, float) #Ideally creates an empty array to deposit the fibonacci numbers in
f = 0.0 #Dummy variable to be edited in the while loop
#Here the first two values of the sequence are defined alongside a counter starting at i = 1
x[0] = 0.0
x[1] = 1.0
i = 1
#While loop which computes the values and writes them to the array x
while f <= 200:
f = x[i]+x[i-1] #calculates the sequence element
i += 1 #Increases the iteration counter by 1 for each loop
x[i] = f #set the array element equal to the calculated sequence number
print(x)
For reference here is a quick terminal output, Ideally I wish to remove the last element:
[ 0. 1. 1. 2. 3. 5. 8. 13. 21. 34. 55. 89.
144. 233.]
There are a number of stylistic points here. Firstly, you should probably use integers, rather than floats. Secondly, you should simply append each number to a list, rather than pre-define an array of a particular size.
Here's an interactive session:
>>> a=[0,1]
>>> while True:
b=a[-1]+a[-2]
if b<=200:
a.append(b)
else:
break
>>> a
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]
Here is a way without using indices:
a = 0
x = [a]
b = 1
while b <= 200:
x.append(b)
a, b = b, a+b
print(x)

Resources