Related
There is a hotel e.g. of size 7x5. I need to create a function where
a number is given as parameter for finding the amount of consecutive empty rooms
returns the number of floor and room number in that.
(depicted below: 0 is empty room and 1 is full)
e.g.:
if the parameter is 1, output will be
"floor no: 5, start from room no: 1"
if the parameter is 2, output will be
"floor no: 5, start from room no: 3"
if the parameter is 3, output will be
"floor no: 5, start from room no: 3"
if the parameter is 4, output will be
"floor no: 4, start from room no: 4"
if the parameter is 5, output will be
"floor no: 2, start from room no: 1"
if the parameter is 6 (or 7), output will be
"floor no: 1, start from room no: 1"
if the parameter is > 7, output will be
"not possible to find in one floor"
preferably without using itertools.grupby.
My try:
def adjacent_rooms (amount):
nested_list_temp = [[0]*7]*5
nested_list = [list(i) for i in nested_list_temp]
nested_list [1][5] = 1
nested_list [2][3] = 1
nested_list [2][4] = 1
nested_list [3][2] = 1
nested_list [4][1] = 1
nested_list [4][5] = 1
# [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 0, 0], [0, 0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 1, 0]]
try:
for i in range(len(nested_list), 0, -1):
for j in range(len(nested_list[0])):
if nested_list[i-1][j] == 0:
count += 1
if count == amount:
return (i, j-amount+2)
else:
count = 0
except:
return "not possible to find in one floor"
Any effective hints or suggestions will be highly appreciated.
Use narrow try-except conditions. The first problem is your all-encompassing
error catching, which hides a wide variety of failures -- including outright
code bugs, such as your failure to initialize count. So the first thing I did
was to delete the try-except. You don't really need it here. And even if you
did, you want to declare one or more specific exceptions in the except clause
rather than leaving it wide open.
Work within Python's list indexing as long as possible. It seems that you
want to return human-oriented floor/room numbers (starting at 1) rather
than computer-oriented numbers (starting at 0). That's fine. But defer the
computer-to-human conversion as long as possible. Within the guts of your
algorithmic code, work with Python's indexing scheme. In your case, your
code straddles both, sometimes using 1-based indexing, sometimes 0-based.
That's confusing.
You are resetting count too often. It should be set whenever the room
is full. But you are resetting it whenever count does not equal amount.
As a result, count is almost always being reset to zero.
You are also resetting count too infrequently. It must be reset at the
start of each new floor.
If we make those changes, we get this:
def adjacent_rooms(nested_list, amount):
for i in range(len(nested_list), 0, -1):
count = 0
for j in range(len(nested_list[0])):
if nested_list[i-1][j] == 0:
count += 1
if count == amount:
return (i, j-amount+2)
else:
count = 0
Python lists are directly iterable. As a result, you almost never
need to mess around with list indexes and range() to process list data.
Just iterate directly to access the values. And for those cases where
you need both the value and the index, use enumerate().
Use more declarative variable names. Names like hotel, floor, and
room help the reader understand your code.
Return data, not textual messages. If a function returns a tuple of
integers upon success, what should it do upon non-serious failure? It depends
on the context, but you can either raise an exception or return some variant of
None. In your case, I would probably opt for a parallel tuple: (None, None). This allows the caller to interact with the function in a fairly
natural way and then simply check either value for None. But returning a
textual message is quite unhelpful for callers: the returned data bundle has a
different outer structure (string vs tuple), and it has a different inner data
type (string vs int).
Don't depend on global variables. Pass the hotel data into
the function, as a proper argument.
If we make those changes, we get something like this:
def adjacent_rooms(hotel, wanted):
for fi, floor in enumerate(hotel):
n = 0
for ri, room in enumerate(floor):
if room == 0:
n += 1
if n == wanted:
return (fi + 1, ri - n + 2)
else:
n = 0
return (None, None)
I'm getting the desired output with slightly different indentation:
nested_list_temp = [[0]*7]*5
nested_list = [list(i) for i in nested_list_temp]
nested_list [1][5] = 1
nested_list [2][3] = 1
nested_list [2][4] = 1
nested_list [3][2] = 1
nested_list [4][1] = 1
nested_list [4][5] = 1
def adjacent_rooms(amount):
for i in range(len(nested_list), 0, -1):
count = 0
for j in range(len(nested_list[0])):
if nested_list[i-1][j] == 0:
count += 1
if count == amount:
return (i, j-amount+2)
else:
count = 0
return "not possible to find in one floor"
Another solution:
def adjacent_rooms(nested_list, amount):
to_search = "0" * amount
for floor in range(len(nested_list) - 1, -1, -1):
try:
idx = "".join(map(str, nested_list[floor])).index(to_search)
return "floor no: {}, start from room no: {}".format(
floor + 1, idx + 1
)
except ValueError:
continue
return "not possible to find in one floor"
nested_list = [[0 for _ in range(7)] for _ in range(5)]
nested_list[1][5] = 1
nested_list[2][3] = 1
nested_list[2][4] = 1
nested_list[3][2] = 1
nested_list[4][1] = 1
nested_list[4][5] = 1
for f in range(1, 10):
print("f={}, result: {}".format(f, adjacent_rooms(nested_list, f)))
Prints:
f=1, result: floor no: 5, start from room no: 1
f=2, result: floor no: 5, start from room no: 3
f=3, result: floor no: 5, start from room no: 3
f=4, result: floor no: 4, start from room no: 4
f=5, result: floor no: 2, start from room no: 1
f=6, result: floor no: 1, start from room no: 1
f=7, result: floor no: 1, start from room no: 1
f=8, result: not possible to find in one floor
f=9, result: not possible to find in one floor
I am trying to convert the given roman numerals to just numerals for programming practice, with the following logic (I dont want to change this logic unless its falsely thought of)
Here,
M - 1000, C-100, X-10, V-5, I-1
example :
Input - MCMXCVI
Expected Result - 1996
logic - 1000 + (1000-100) + (100-10) + 5 + 1
index- 1 + (3-2) + (5-4) + 6 + 7
Here i am searching next value from the current value subtracting it if its not greater we are adding it normally.
Here is what i have tried, i could't code it correctly, having spent lot of time, thought to ask out for help.
def roman_numeral(num):
"""
Write a Python class to convert an roman numeral to a integer.
Logic: https://www.rapidtables.com/convert/number/how-roman-numerals-to-number.html
"""
# Input the string
# Map of roman numerals and the corresponding values in a dictionary.
NUMERALS = {1000:'M', 900:'CM', 500:'D', 400:'CD', 100:'C', 90:'XC',
50:'L', 40:'XL', 10:'X', 9:'IX', 5:'V', 4:'IV', 1:'I'}
retval=[]
#Check if each char matches with the dictionary and take the numerical value of the inputed roman
for k in range(len(num)):
for i,j in NUMERALS.items():
if(j==num[k]):
retval.append(i)
elm_count = len(retval)
result=0
result_less=0
result_more=0
ind_tracker=0
#Check if next char from the position of current char if that numerical value is greater then current numerical value.
#If it is greater subtract the current numeric value, if not greater then add it.
for ind,i in enumerate(retval):
print('ind= ',ind,'i= ', i)
#Using this below condition to skip if we have already subtracted the current value from previous value.
if( ind_tracker>ind):
continue
if((ind+1 < elm_count)):
if(i<retval[ind+1]):
#print('result=',result,'retval[ind]=',retval[ind],'retval[ind+1]=', retval[ind+1])
result_less=retval[ind+1]-retval[ind]
print('result_less=',result_less)
ind_tracker=ind+1
else:
result_more+=retval[ind]+result_less
print('result_more=',result_more)
result=result_more
print('final result= ',result)
return result
roman_numeral('MCMXCVI')
The output im getting is
3185
I expect to get
1996
You can change the basic concept. If you reverse the roman numbers and basically start from the right side of the string the whole thing get really simple.
The idea is that if you start from the right, if the next number is bigger or equal to the current number you add the number to the total, if the next number is smaller than the previous one then it is substraced from the total.
roman = "MCMXCVI"
NUMERALS = {1000:'M', 900:'CM', 500:'D', 400:'CD', 100:'C', 90:'XC',
50:'L', 40:'XL', 10:'X', 9:'IX', 5:'V', 4:'IV', 1:'I'}
# reverse roman number letters (basically start from the end
roman_reversed = list(reversed(roman))
#invert the dictionary because we want to translate the letter to numbers not the other way around
inverse_NUMERALS = {v: k for k, v in NUMERALS.items()}
# get the number for each character on its own:
lst_numbers = [inverse_NUMERALS.get(x) for x in roman_reversed]
# loop through the list of numbers
total = 0
previous = 0
for numb in lst_numbers:
if numb >= previous:
total += numb
else:
total -= numb
previous = numb
print(total)
#Out[21]: 1996
You could use (self-implemented):
class RomanToDecimal:
conversion = {'M': 1000, 'CM': 900, 'D': 500, 'CD': 400, 'C': 100, 'XC': 90, 'L': 50, 'XL': 40, 'X': 10, 'IX': 9,
'V': 5, 'IV': 4, 'I': 1}
def convert(self, roman):
total = 0
while len(roman):
before = len(roman)
for key in self.conversion:
if roman.startswith(key):
total += self.conversion[key]
roman = roman[len(key):]
after = len(roman)
if before == after:
raise ValueError("Not a Roman numeral.")
return total
try:
rtd = RomanToDecimal()
assert rtd.convert('M') == 1000
assert rtd.convert('XXXVI') == 36
assert rtd.convert('MMXII') == 2012
assert rtd.convert('MMXX') == 2020
except ValueError as error:
print(error)
I made some minor changes to your existing code!
I added a variable "flag" set to False on default and when result_less, I set it to true
To check we subtracted or not, I used this flag, when the flag is true, i made it false and skipped an iternation.
added a new if statement to check for last number
on result_more, result+=retval[ind], did not used result_less value
on result_less, result+=retval[ind+1]-retval[ind]. in both cases i changed the result value rather than changing more and less values for simplicity.
and BTW, I got rid of those result_more and result_less variables but kept the print statements.
here is your code, modified:
def roman_numeral(num):
"""
Write a Python class to convert an roman numeral to a integer.
Logic: https://www.rapidtables.com/convert/number/how-roman-numerals-to-number.html
"""
# Input the string
# Map of roman numerals and the corresponding values in a dictionary.
NUMERALS = {1000:'M', 900:'CM', 500:'D', 400:'CD', 100:'C', 90:'XC',
50:'L', 40:'XL', 10:'X', 9:'IX', 5:'V', 4:'IV', 1:'I'}
retval=[]
#Check if each char matches with the dictionary and take the numerical value of the inputed roman
for k in range(len(num)):
for i,j in NUMERALS.items():
if(j==num[k]):
retval.append(i)
elm_count = len(retval)
result=0
result_less=0
result_more=0
# ind_tracker=0
flag = False
#Check if next char from the position of current char if that numerical value is greater then current numerical value.
#If it is greater subtract the current numeric value, if not greater then add it.
for ind,i in enumerate(retval):
print('ind= ',ind,'i= ', i)
#Using this below condition to skip if we have already subtracted the current value from previous value.
# if( ind_tracker>ind):
# continue
if(flag):
print("Skipped! Already Subracted!")
flag=False
continue
if((ind+1 == elm_count)):
# if last digit is greater than it's previous, the flag will skip this iteration
print('last digit=',retval[ind])
result+=retval[ind]
if((ind+1 < elm_count)):
if(i<retval[ind+1]):
#print('result=',result,'retval[ind]=',retval[ind],'retval[ind+1]=', retval[ind+1])
# result_less=retval[ind+1]-retval[ind]
result+=retval[ind+1]-retval[ind]
print('result_less=',retval[ind+1]-retval[ind])
# ind_tracker=ind+1
flag = True
else:
# result_more+=retval[ind]+result_less
result+=retval[ind]
print('result_more=',retval[ind])
# result=result_more
print('final result= ',result)
return result
roman_numeral('MCMXCVI')
I am trying to write a code that gives the mode of the data. And I shouldn't use functions available under the statistics
module but I can use built-in functions such as max,min,sum,etc.
list1 = [1,2,3,4,5,4,3,2,1,2,3,3,4,2,1,3,2,1,3,5]
time=0
freq1=0
freq2=0
freq3=0
freq4=0
freq5=0
for i in list1:
if list1[i]==1:
time+=time
freq1=time
elif list1[i]==2:
time+=time
freq2=time
elif list1[i]==3:
time+=time
freq3=time
elif list1[i]==4:
time+=time
freq4=time
elif list1[i]==5:
time+=time
freq5=time
list2=[freq1,freq2,freq3,freq4,freq5]
print(max(list2))
Here is what I get
0
>>>
What am I doing wrong?
Your approach is wrong for a number of reasons. First of all, you should not need time for the program you wrote. Second, you are adding time to itself everytime. now time is 0, so even after multiple additions, you are getting 0. Third, you are iterating over all the elements, not their indices, so instead of checking for list[i] you need to check for i. So it should have been something like this:
list1 = [1,2,3,4,5,4,3,2,1,2,3,3,4,2,1,3,2,1,3,5]
freq1=0
freq2=0
freq3=0
freq4=0
freq5=0
for i in list1:
if i==1:
freq1 += 1
elif i==2:
freq2 += 1
elif i==3:
freq3 += 1
elif i==4:
freq4 += 1
elif i==5:
freq5 += 1
list2=[freq1,freq2,freq3,freq4,freq5]
print(max(list2))
But even after all this you will get the maximum occurrence of a value, not the value which occurs the maximum time. The result of the above program is 6. That is the count of the mode 3 that occurs 6 times. Now, in order to get the mode itself, you need some other data structure, either list of lists, or list of tuples, but the best is dictionary.
So something like this would be the sweetest:
list1 = [1,2,3,4,5,4,3,2,1,2,3,3,4,2,1,3,2,1,3,5]
d = {}
for num in list1:
d[num] = d.get(num,0) + 1
# at this point, d looks like this:
# {1: 4, 2: 5, 3: 6, 4: 3, 5: 2}
# Then either:
print('mode =',max(d,key = lambda key:d[key]))
# outputs:
# mode = 3
# or
print('mode = {0}, occurs {1} times'.format(*max(list(d.items()),key = lambda item:item[1])))
# outputs:
# mode = 3, occurs 6 times
You are adding time to time. time = 0. Time + time = 0 So the freq values are wrong.
Fixing this by using:
time+=1
Will not work either unless you initiate a new time for each of your possible variables. You could just use freqn+=1 for each of your cases and it would work the same.
There are much more efficient ways to do this but thats the error in your code.
I have three lists, each one with several possible values.
probs = ([0.1,0.1,0.2], \
[0.7,0.9], \
[0.5,0.4,0.1])
I want to test all possible combinations of choosing one element from each list. So, 3*2*3=18 possible combinations in this example. In the end, I want to choose the most favourable combinations according to some criteria. This is:
[<index in row 0> , <index in row 1> , <index in row 2> , <criteria value>]
I can accomplish my task by using three nested for loops (which I did). However, in the real application of this code, I will have a variable number of lists. Because of that, it seems the solution would be using a recursive function with a for loop inside it (which I did as well). The code:
# three rows. Test all combinations of one element from each row
# This is [value form row0, value from row1, value from row2]
# So: 3*2*3 = 18 possible combinations
probs = ([0.1,0.1,0.2], \
[0.7,0.9], \
[0.5,0.4,0.1])
meu = [] # The list that will store the best combinations in the recursion
#######################################################
def main():
choice = [] #the list that will store the best comb in the nested for
# accomplish by nested for loops
for n0 in range(len(probs[0])):
for n1 in range(len(probs[1])):
for n2 in range(len(probs[2])):
w = probs[0][n0] * probs[1][n1] * probs[2][n2]
cmb = [n0,n1,n2,w]
if len(choice) == 0:
choice.append(cmb)
elif len(choice) < 5:
for i in range(len(choice)+1):
if i == len(choice):
choice.append(cmb)
break
if w < choice[i][3]:
choice.insert(i,cmb)
break
else:
for i in range(len(choice)):
if w < choice[i][3]:
choice.insert(i,cmb)
del choice[-1]
break
# using recursive function
combinations(0,[])
#both results
print('By loops:')
print(choice)
print('By recursion:')
print(meu)
#######################################################
def combinations(step,cmb):
# Why does 'meu' needs to be global
if step < len(probs):
for i in range(len(probs[step])):
cmb = cmb[0:step] # I guess this is the same problem I dont understand recursion
# But, unlike 'meu', here I could use this workaround
cmb.append(i)
combinations(step+1,cmb)
else:
w = 1
for n in range(len(cmb)):
w *= probs[n][cmb[n]]
cmb.append(w)
if len(meu) == 0:
meu.append(cmb)
elif len(meu) < 5:
for i in range(len(meu)+1):
if i == len(meu):
meu.append(cmb)
break
if w < meu[i][-1]:
meu.insert(i,cmb)
break
else:
for i in range(len(meu)):
if w < meu[i][-1]:
meu.insert(i,cmb)
del meu[-1]
break
return
######################################################
main()
It outputs, as I wanted:
By loops:
[[0, 0, 2, 0.006999999999999999], [1, 0, 2, 0.006999999999999999], [0, 1, 2, 0.009000000000000001], [1, 1, 2, 0.009000000000000001], [2, 0, 2, 0.013999999999999999]]
By recursion:
[[0, 0, 2, 0.006999999999999999], [1, 0, 2, 0.006999999999999999], [0, 1, 2, 0.009000000000000001], [1, 1, 2, 0.009000000000000001], [2, 0, 2, 0.013999999999999999]]
Initially, I wanted to use the 'meu' list as internal of the function, because, I thought, it would be better to avoid global variables (perhaps not... I'm a newbie). The problem was I could not come up with a code that would pass both 'meu' and 'cmb' between depths to give the same effect of the nested loops.
How could I implement a recursive function with internal 'meu' instead of being a global list? What am I missing from recursion concept? Thanks.
++++++++++++++++++++++++++++++++++
Example of a failed function:
def combinations(choice,step,cmb):
if step < len(probs):
for i in range(len(probs[step])):
cmb = cmb[0:step] #workaroud for cmb
cmb.append(i)
choice = combinations(choice,step+1,cmb)
else:
w = 1
for n in range(len(cmb)):
w *= probs[n][cmb[n]]
cmb.append(w)
if len(choice) == 0:
choice.append(cmb)
elif len(choice) < 5:
for i in range(len(choice)+1):
if i == len(choice):
choice.append(cmb)
break
if w < choice[i][-1]:
choice.insert(i,cmb)
break
else:
for i in range(len(choice)):
if w < choice[i][-1]:
choice.insert(i,cmb)
del choice[-1]
break
return choice
Called by:
choice = combinations([],0,[])
Don't reinvent the wheel (recursively or not): use the included batteries. The problem you are trying to solve is extremely common and so a solution is included in Python's standard library.
What you want—every combination of every value from some number of lists—is called the Cartesian product of those lists. itertools.product exists to generate those for you.
import itertools
probs = ([0.1, 0.1, 0.2],
[0.7, 0.9],
[0.5, 0.4, 0.1])
for prob in itertools.product(*probs):
print prob
# prob is a tuple containing one combination of the variables
# from each of the input lists, do with it what you will
If you want to know what index each item comes from, the easiest way is to just pass the indices to product() rather than the values. You can easily get that using range().
for indices in itertools.product(*(range(len(p)) for p in probs)):
# get the values corresponding to the indices
prob = [probs[x][indices[x]] for x in range(len(probs))]
print indices, prob
Or you could use enumerate() -- this way, each item in the product is a tuple containing its index and its values (not two separate lists the way you get them in the above method):
for item in itertools.product(*(enumerate(p) for p in probs)):
print item
My code below is getting stuck on a random point:
import functions
from itertools import product
from random import randrange
values = {}
tables = {}
letters = "abcdefghi"
nums = "123456789"
for x in product(letters, nums): #unnecessary
values[x[0] + x[1]] = 0
for x in product(nums, letters): #unnecessary
tables[x[0] + x[1]] = 0
for line_cnt in range(1,10):
for column_cnt in range(1,10):
num = randrange(1,10)
table_cnt = functions.which_table(line_cnt, column_cnt) #Returns a number identifying the table considered
#gets the values already in the line and column and table considered
line = [y for x,y in values.items() if x.startswith(letters[line_cnt-1])]
column = [y for x,y in values.items() if x.endswith(nums[column_cnt-1])]
table = [x for x,y in tables.items() if x.startswith(str(table_cnt))]
#if num is not contained in any of these then it's acceptable, otherwise find another number
while num in line or num in column or num in table:
num = randrange(1,10)
values[letters[line_cnt-1] + nums[column_cnt-1]] = num #Assign the number to the values dictionary
print(line_cnt) #debug
print(sorted(values)) #debug
As you can see it's a program that generates random sudoku schemes using 2 dictionaries : values that contains the complete scheme and tables that contains the values for each table.
Example :
5th square on the first line = 3
|
v
values["a5"] = 3
tables["2b"] = 3
So what is the problem? Am I missing something?
import functions
...
table_cnt = functions.which_table(line_cnt, column_cnt) #Returns a number identifying the table considered
It's nice when we can execute the code right ahead on our own computer to test it. In other words, it would have been nice to replace "table_cnt" with a fixed value for the example (here, a simple string would have sufficed).
for x in product(letters, nums):
values[x[0] + x[1]] = 0
Not that important, but this is more elegant:
values = {x+y: 0 for x, y in product(letters, nums)}
And now, the core of the problem:
while num in line or num in column or num in table:
num = randrange(1,10)
This is where you loop forever. So, you are trying to generate a random sudoku. From your code, this is how you would generate a random list:
nums = []
for _ in range(9):
num = randrange(1, 10)
while num in nums:
num = randrange(1, 10)
nums.append(num)
The problem with this approach is that you have no idea how long the program will take to finish. It could take one second, or one year (although, that is unlikely). This is because there is no guarantee the program will not keep picking a number already taken, over and over.
Still, in practice it should still take a relatively short time to finish (this approach is not efficient but the list is very short). However, in the case of the sudoku, you can end up in an impossible setting. For example:
line = [6, 9, 1, 2, 3, 4, 5, 8, 0]
column = [0, 0, 0, 0, 7, 0, 0, 0, 0]
Where those are the first line (or any line actually) and the last column. When the algorithm will try to find a value for line[8], it will always fail since 7 is blocked by column.
If you want to keep it this way (aka brute force), you should detect such a situation and start over. Again, this is very unefficient and you should look at how to generate sudokus properly (my naive approach would be to start with a solved one and swap lines and columns randomly but I know this is not a good way).