Navigating the robot by finding the shortest way - python

I have this task where I have to make my robot find the shortest way when I enter a destination point. I've created some functions to assign numbers (distances) for each square and counts its way back to my robot by deleting the other options. Then the robot should only follow the numbers.
My code works so far, however I think I should be able to reach the same outcome by using less for loops and with a more efficient writing. I believe if I see different ways of thinking in my earlier stages, I can have a broader perspective in the future. So, any ideas on how to reach my goal with a shorter code?
#Your Code Starts Here
"""
for x in range(0, map.width):
for y in range(0, map.height):
if map.blocked(x, y):
map.value[x, y] = -1
else:
map.value[x, y] = 0
"""
def fill_around(n,m,number):
for x in range (n-1,n+2):
for y in range (m-1,m+2):
if map.value[x,y] != 0:
pass
elif x==n+1 and y==m+1 or x==n-1 and y==m-1 or x==n-1 and y==m+1 or x==n+1 and y==m-1:
pass
elif map.blocked(x,y) == True:
map.value[x,y]= -1
elif x==n and y==m:
map.value[x,y]= number
else:
map.value[x,y]= number+1
def till_final(final_x,final_y):
final_x=9
final_y=1
fill_around(1,1,1)
for p in range(2,17):
for k in range(0, map.width):
for l in range(0, map.height):
if map.value[final_x,final_y] ==0 and map.value[k,l]==p:
fill_around(k,l,p)
def delete_duplicates(final_x,final_y):
for k in range(0, map.width):
for l in range(0, map.height):
if map.value[k,l] == map.value[final_x,final_y] and k != final_x and l != final_y:
map.value[k,l] = 0

Related

Understanding speed in Python through a competition problem exceeding time limits

I recently have been doing some competition problems. In particular this one:
https://open.kattis.com/problems/knightjump
An abridged statement of the problem:
You are given a two dimensional (square) chess board of size N (1-based indexing). Some of the cells on this board are ‘.’ denoting an empty cell. Some of the cells on this board are ‘#’ denoting a blocked cell, which you are not allowed to visit. Exactly one of the cells on this board is ‘K’ denoting the initial position of the knight. Determine the minimum number of steps required for the Knight to reach cell (1,1) while avoiding cells with ‘#’ in the path.
I know this is a simple BFS problem and have written the following code to solve it:
from collections import deque
from sys import stdin
dirs = ((2,1), (2,-1), (-2,1), (-2,-1), (1,2), (1,-2), (-1,2), (-1,-2))
N = int(stdin.readline())
q = deque()
mat = [list(str(stdin.readline())) for _ in range(N)]
for i in range(N):
for j in range(N):
if mat[i][j] == "K":
inX, inY = i+1, j+1
break
q.append((inX,inY,0))
flag = False
visited = [[0]*N for _ in range(N)]
while q:
ix,iy, moves = q.popleft()
visited[ix-1][iy-1] = 1
if (ix,iy) == (1,1):
print(moves)
flag = True
break
for d in dirs:
dx,dy = d
if 1 <= ix + dx < N+1 and 1 <= iy + dy < N+1 and mat[ix+dx-1][iy+dy-1] != "#" and visited[ix+dx-1][iy+dy-1] == 0:
q.append((ix+dx,iy+dy, moves+1))
if not flag:
print(-1)
All test data I've generated has given a correct answer, but I am recieving a time limit error on the site for some cases (and correct on all other). The input limits are N <= 100. I know that C++ is faster, but I was hoping to use this problem to learn a bit about why my solution times out but many Python solutions on a similar concept perform quick, and hopefully teach me something about Python nuts and bolts I didn't know. For example - I found this solution on someone's Github:
from collections import deque
n = int(input())
grid = [list(input()) for _ in range(n)]
for i in range(n):
for j in range(n):
if grid[i][j] == 'K':
k_i, k_j = i, j
break
visited = [[0 for x in range(n)] for y in range(n)]
# time complexity is number of configs (n^2) multiplied by
# work per configs (iterate through 8 deltas)
def k_jumps(i, j):
q = deque()
q.append((0, i, j))
visited[i][j] = 1
valid = False
while len(q) > 0:
d, i, j = q.popleft()
if (i,j) == (0,0):
print(d)
valid = True
break
deltas = {(-2, 1), (-2, -1), (1, 2), (1, -2), (-1, 2), (2, 1), (2, -1), (-1, -2)}
for delta in deltas:
di, dj = delta
if 0 <= i + di < n and 0 <= j + dj < n and visited[i+di][j+dj] == 0 and grid[i+di][j+dj] != '#':
visited[i+di][j+dj] = 1
q.append((d+1, i+di, j+dj))
if not valid:
print(-1)
k_jumps(k_i, k_j)
(Thanks to user Case Pleog).
Superficially the solutions are the same (BFS) - however the second solution runs in 0.07s, while my solutions times out at over 1s.
While there are some minor differences between our solutions, I can't see what explains such a large time discrepancy. I already made some changes to my code to see if that was the issue, e.g. the line:
if 1 <= ix + dx < N+1 and 1 <= iy + dy < N+1 and mat[ix+dx-1][iy+dy-1] != "#" and visited[ix+dx-1][iy+dy-1] == 0:
and indeed that speeded things up, as before I was using:
if min(point) > 0 and max(point) < N+1 and mat[point[0]+dx-1][point[1]+dy-1] != "#" and visited[point[0]+dx-1][point[1]+dy-1] == 0:
where point was a tuple consisting of ix,iy. Any advice would be appreciated - I would like to know more about what's causing the time difference as its > 10x difference. This is just a hobby, so some of my code may have amateur quirks/inefficiencies which hopefully I can explain. Thanks!
Do visited[ix+dx-1][iy+dy-1] = 1 right away when you do q.append((ix+dx,iy+dy, moves+1)), otherwise you might put that same coordinate into the queue multiple times.

Combination recursive - Backtracking

I've been trying to code 3 different recursive function(Backtracking). The first one should show all permutations(orders doesn't matters) and there's a specif size. The second one should show all cyclic permutations. The third one should show all permutations(orders matters) and there's a specif size.
I was able to do the first one by myself:
def Arranjo(list, repeat):
if repeat <= 0:
return [[]]
solution = []
for i in range(0, len(list)):
remainingList = list[:i] + list[i + 1 :]
for j in Arranjo(remainingList, repeat - 1):
solution.append([list[i]] + j)
return solution
# 0 to 100
list = [x for x in range(1, 101)]
print(Arranjo(list, 3))
I've been facing some issues in the other two exercises. I've already spent hours trying to do the second exercise, but I don't have any idea about how to do it. That's what I was able to do:
def PermutacaoCircular(list):
if len(list) == 0:
return
elif len(list) == 1:
return list
else:
solution = []
sub_solution = []
for i in range(len(list)):
// code here
return solution
list = [1, 2, 3]
print(PermutacaoCircular(list))
The third one, I don't even know how to start.

Why is this code not running fully? It doesn't run line 53

I made myself an exercise with python since I am new. I wanted to make a rever LMC calculator ( Least common multiple ) but for some reason, something as simple as a print in a loop doesn't seem o work for me. I would appreciate some help since I am stuck on this weird issue for 20 minutes now. Here is the code:
import random
import sys
def print_list():
count_4_print = 0
while count_4_print < len(values):
print(values[count_4_print])
count_4_print += 1
def lcm(x, y):
if x > y:
greater = x
else:
greater = y
while True:
if (greater % x == 0) and (greater % y == 0):
lcm1 = greater
break
greater += 1
return lcm1
def guess(index, first_guess, second_guess):
num = 1
while lcm(first_guess, second_guess) != values[num - 1]:
first_guess = random.randrange(1, 1000000)
second_guess = random.randrange(1, 1000000)
num += 1
num = 1
if lcm(first_guess, second_guess) == values[num - 1]:
return first_guess, second_guess
num += 1
lineN = int(input())
values = []
count_4_add = 0
count_4_guess = 0
for x in range(lineN):
values.append(int(input()))
count_4_add += 1
if count_4_add >= lineN:
break
print_list()
for x in range(lineN + 1):
first, second = guess(count_4_guess, 1, 1)
count_4_guess += 1
print(first + second)
# this ^^^ doesn't work for some reason
Line 57 is in the while loop with count_4_guess. Right above this text, it says print(first_guess + second_guess)
Edit: The code is supposed to take in an int x and then prompt for x values. The outputs are the inputs without x and LMC(output1, output2) where the "LMC" is one of the values. This is done for each of the values, x times. What it actually does is just the first part. It takes the x and prompts for x outputs and then prints them but doesn't process the data (or it just doesn't print it)
Note: From looking at your comments and edits it seems that you are lacking some basic knowledge and/or understanding of things. I strongly encourage you to study more programming, computer science and python before attempting to create entire programs like this.
It is tough to answer your question properly since many aspects are unclear, so I will update my answer to reflect any relevant changes in your post.
Now, onto my answer. First, I will go over some of your code and attempt to give feedback on what could improved. Then, I will present two ways to compute the least common multiple (LCM) in python.
Code review
Code:
def print_list():
count_4_print = 0
while count_4_print < len(values):
print(values[count_4_print])
count_4_print += 1
Notes:
Where are the parameters? It was already mentioned in a few comments, but the importance of this cannot be stressed enough! (see the note at the beginning of my comment)
It appears that you are trying to print each element of a list on a new line. You can do that with print(*my_list, sep='\n').
That while loop is not how you should iterate over the elements of a list. Instead, use a for loop: for element in (my_list):.
Code:
def lcm(x, y):
if x > y:
greater = x
else:
greater = y
while True:
if (greater % x == 0) and (greater % y == 0):
lcm1 = greater
break
greater += 1
return lcm1
Notes:
This is not a correct algorithm for the LCM, since it crashes when both numbers are 0.
The comparison of a and b can be replaced with greater = max(x, y).
See the solution I posted below for a different way of writing this same algorithm.
Code:
def guess(index, first_guess, second_guess):
num = 1
while lcm(first_guess, second_guess) != values[num - 1]:
first_guess = random.randrange(1, 1000000)
second_guess = random.randrange(1, 1000000)
num += 1
num = 1
if lcm(first_guess, second_guess) == values[num - 1]:
return first_guess, second_guess
num += 1
Notes:
The line num += 1 comes immediately after return first_guess, second_guess, which means it is never executed. Somehow the mistakes cancel each other out since, as far as I can tell, it wouldn't do anything anyway if it were executed.
if lcm(first_guess, second_guess) == values[num - 1]: is completely redundant, since the while loop above checks the exact same condition.
In fact, not only is it redundant it is also fundamentally broken, as mentioned in this comment by user b_c.
Unfortunately I cannot say much more on this function since it is too difficult for me to understand its purpose.
Code:
lineN = int(input())
values = []
count_4_add = 0
count_4_guess = 0
for x in range(lineN):
values.append(int(input()))
count_4_add += 1
if count_4_add >= lineN:
break
print_list()
Notes:
As explained previously, print_list() should not be a thing.
lineN should be changed to line_n, or even better, something like num_in_vals.
count_4_add will always be equal to lineN at the end of your for loop.
Building on the previous point, the check if count_4_add >= lineN is useless.
In conclusion, count_4_add and count_4_guess are completely unnecessary and detrimental to the program.
The for loop produces values in the variable x which is never used. You can replace an unused variable with _: for _ in range(10):.
Since your input code is simple you could probably get away with something like in_vals = [int(input(f'Enter value number {i}: ')) for i in range(1, num_in_vals+1)]. Again, this depends on what it is you're actually trying to do.
LCM Implementations
According to the Wikipedia article referenced earlier, the best way to calculate the LCM is using the greatest common denominator.
import math
def lcm(a: int, b: int) -> int:
if a == b:
res = a
else:
res = abs(a * b) // math.gcd(a, b)
return res
This second method is one possible brute force solution, which is similar to how the one you are currently using should be written.
def lcm(a, b):
if a == b:
res = a
else:
max_mult = a * b
res = max_mult
great = max(a, b)
small = min(a, b)
for i in range(great, max_mult, great):
if i % small == 0:
res = i
break
return res
This final method works for any number of inputs.
import math
import functools
def lcm_simp(a: int, b: int) -> int:
if a == b:
res = a
else:
res = abs(a * b) // math.gcd(a, b)
return res
def lcm(*args: int) -> int:
return functools.reduce(lcm_simp, args)
Oof, that ended up being way longer than I expected. Anyway, let me know if anything is unclear, if I've made a mistake, or if you have any further questions! :)

Python nested loops - no output

I've just started learning python. I am trying to check if the integer x is palindrome then divide it by a number between range (starting from largest y i.e. 999 ) y=999,998,...,100. If x/y=z and z is also a 3 digit integer then finish. Otherwise subtract 1 from x and do the same procedure.
def EuQ4():
x=998001
p=999
while 10000 < x:
x=x-1
if str(x)== str(x)[::-1]:
while p>100:
if x%p==0:
Pal=x/p
if Pal < 999:
print (Pal,p)
break
else:
x=x-1
else:
p=p-1
else:
x=x-1
EuQ4()
This is question 4 from Project Euler i.e. Find the largest palindrome made from the product of two 3-digit numbers.
You have a few logic errors in here. Some cause loops that just never end. For example, what happens when x % p == 0 but Pal is larger 999? You would get an infinite loop.
I made a few modifications, but it could still use some work.
def EuQ4():
x = 998001
while 10000 < x:
if str(x) == str(x)[::-1]:
print("{} is a pali!".format(x))
# Move it here so each time it stats at the right
# number or else it will just skip it after it does it once.
p = 999
while p > 100:
if x % p == 0:
pali = int(x / p)
if pali < 999:
print(pali, p)
return
p -= 1
x -= 1
EuQ4()
Edit:
I found these mistakes by using the debugger in my IDE. You could have easily done the same thing by going through the code line by line a few times.
I am sorry but it was hurting my head to read your question. If you are trying to learn Python while attempting these questions then I would propose this alternate answer - it does not answer your question but it does lead to the solution and I think it is more Pythonic. The question asks to find the largest palindrone made from the product of two 3 digit numbers. So the inputs should be 3 digit numbers. This code will allow you to specify the number of digits, max and min (as integers).
I am not proposing that this be the best solution the the Euler Problem posed rather it is a solution that gives you exposure to a range of features in Python.
def min_value(integer):
min_val = '1'
for n in range(0,integer-1):
min_val+='0'
return int(min_val)
def max_value(integer):
max_val = '9'
for n in range(0,integer-1):
max_val += '9'
return int(max_val) +1
def find_max_palindrones(x,y):
minimum_value = min_value(x)
maximum_value = max_value(y)
palindrones = []
working_range = [number for number in range(minimum_value,maximum_value,1)]
for x_value in working_range:
for y_value in working_range:
product = x_value * y_value
orig_order = [item for item in str(product)]
rev_order = [item for item in str(product)[::-1]]
if orig_order == rev_order:
palindrones.append(product)
max_p = max(palindrones)
return max_p
>>>find_max_palindrones(3,3)
906609
Put p=999 befor while p > 100 or use for p in range(999, 100, -1).
p = 999
while p > 100
And I think you call x=x-1 too many times.

segmentation fault, need to prevent this and reduce the run time of my program

I solved a problem where I had to find sum of numbers whose digits are made of only 4, 5 and 6.. The numbers have at most x fours, at most y fives and at most z sixes. I successfully passed some of the sample tests. But, I cannot get past other test cases as i keep getting segfault errors. Also, I think my programs run time is seriously long. Any help in reducing the run time, optimizing the solution and -reventing segfault will be appreciated.
Here is my code in Python:
from itertools import permutations
x , y, z = raw_input().split(' ')
x = int(x)
y = int(y)
z = int(z)
max = ''
for a in range(z):
max += '6'
for b in range(y):
max += '5'
for c in range(x):
max += '4'
perms = [''.join(p) for p in permutations(max)]
chances = []
def substring(string):
possible = []
for x in range(len(string) + 1):
for y in range(x, len(string) + 1):
possible.append(string[x:y])
return possible
for d in perms:
chances += list(set(substring(d)))
chances = list(set(chances))
chances.remove('')
sum = 0
for nstr in chances:
sum += int(nstr)
print sum
It would be helpful to know what part of the program is consuming the most time. Looking at the second half, after the call to permutations, I see that you are creating potentially huge lists (in chances and permutations. After building them you convert to a set (to eliminate duplicates I suppose) and back again to a list. Why not use a single set instead, like this:
chances = set()
def substring(string):
for x in range(len(string) + 1):
for y in range(x, len(string) + 1):
chances.add(string[x:y])
for d in perms:
substring(d)
chances.remove('')
sum = 0
for nstr in chances:
sum += int(nstr)
print sum
I don't know if that will solve all your problems, but it should help.

Categories