Insertion algorithm can not insert all nodes - python

I am newbie in Python. I want to insert new node into the current route to check whether it makes the route shorter or not. However my code doesn't run well, please show me the mistake. The steps are following:
1. Create random subtour (example: 0-2-0)
2. Get randomly the node which is not visited and check this node in each pair of nodes in current route. If the node satisfy the shorter requirement, we insert it into current node (example: 0-4-2-0).
3. Continue until all nodes inserted into the route.
import random
distMatrix = [
[100, 14, 20, 10, 35, 18, 5],
[6, 100, 7, 35, 17, 9, 24],
[8, 35, 100, 36, 27, 3, 15],
[21, 7, 12, 100, 7, 4, 26],
[33, 25, 6, 18, 100, 19, 11],
[6, 2, 22, 30, 9, 100, 8],
[24, 3, 12, 5,17, 16, 100],
]
def get_total_distance(route,d):
total = 0
for i in range (len(route)-1):
pre = route[i]
succ = route[i+1]
total += d[pre][succ]
return total
def insertion(d):
numNodes = len(d)
notVisited = list(range(1, numNodes))
first_random_node = random.choice(notVisited)
route = [0]
route.append(first_random_node)
notVisited.remove(first_random_node)
route.append(0) #create first subtour
print("1st",route)
location = 0
while len(notVisited) != 0:
for j in notVisited:
for i in range (len(route)-1):
pre = route[i]
succ = route[i+1]
check_route = d[pre][j] + d[j][succ]
current_distance = d[pre][succ]
if check_route <= current_distance:
print(j)
route.insert(i + 1, j)
notVisited.remove(j)
print("2nd", route)
return route
solution = insertion(distMatrix)
print("The solution for the route is:",solution)
print("The total distance is:", get_total_distance(solution,distMatrix))

As it is, your code doesn't run: there's a syntax error because you named a local function the same as a package you're trying to use: random. Inside your function random, you can no longer access the package of that name, because you redefined the name.
Once that is resolved, your code hangs with this output:
1st [0, 5, 0]
3
2nd [0, 3, 5, 0]
6
2nd [0, 6, 3, 5, 0]
You have a logic problem with this combination:
while len(notVisited) != 0:
print("WHILE", len(notVisited))
for j in notVisited:
....
if check_route <= current_distance:
print(j)
route.insert(i + 1, j)
notVisited.remove(j)
print("2nd", route)
Your only exit from the while loop is to remove everything from notVisited. There's no logical guarantee that will happen -- and when it doesn't, you're stuck in an infinite loop trying to find a shorter route that doesn't exist.

Related

If/else statement with a triplet that sum to a given value

I'm writing a sum up game where two players will take turns picking a random number in the range (1,9), no repeated number allowed. So I'm struggling at
If at any point exactly three of the player's numbers sum to 15, then that player has won.
If the first player picks [7, 2, 3, 5], he will win because 7+3+5 = 15
So my question is why doesn't the program stop when first_player has inputs == 15
I want to avoid importing any libs.
Instead of generating all permutations at each step, maintain a map of what each permutation sums to, then add two branches to each branch at each move.
Think of each entry as a set of bits, ie with each permutation you either include a given entry or not, eg if the numbers are [7, 3, 2] you might store [1, 0, 1] for the combination of the 7 and the 2.
You can make a hashmap of 101->9 etc and when someone adds a 3 to it you add an entry for 1010->9 and 1011->12. As soon as you see the target you know the game is over.
So the evolution of [7, 3, 2] would be
0->0
1->7
00->0
01->3
10->7
11->10
000->0
001->2
010->3
011->5
100->7
101->9
110->10
111->12
A more efficient way would be to find only those numbers whose sum is equal to the target that is 15.
entry = [7, 5, 1, 3]
def is_sum_15(nums):
res = []
search_numbers(nums, 3, 15, 0, [], res)
return len(res) != 0
def search_numbers(nums, k, n, index, path, res):
if k < 0 or n < 0:
return
if k == 0 and n == 0:
res.append(path)
for i in range(index, len(nums)):
search_numbers(nums, k-1, n-nums[i], i+1, path+[nums[i]], res)
print(is_sum_15(entry)) # True
An inefficient but easy way is to use itertools.permutations:
>>> entry = [7, 2, 3, 5]
>>> import itertools
>>> [sum(triplet) for triplet in itertools.permutations(entry, r=3) if sum(tr]
[12, 14, 12, 15, 14, 15, 12, 14, 12, 10, 14, 10, 12, 15, 12, 10, 15, 10, 14, 15, 14, 10, 15, 10]
>>> any(sum(triplet) == 15 for triplet in itertools.permutations(entry, r=3))
True
It's inefficient, because you would be trying all permutations every time entry gets expanded with a new number.

Fails on returning empty array for [0,0] sum

Im trying to test function with that tests, btw its always fails with:
[0, 0] should equal []
Can someone tell please where is my mistake ?
Tests:
Test.describe("Basic tests")
Test.assert_equals(count_positives_sum_negatives([1, 2, 3, 4, 5, 6, 7,
8, 9, 10, -11, -12, -13, -14, -15]),[10,-65])
Test.assert_equals(count_positives_sum_negatives([0, 2, 3, 0, 5, 6, 7,
8, 9, 10, -11, -12, -13, -14]),[8,-50])
Test.assert_equals(count_positives_sum_negatives([1]),[1,0])
Test.assert_equals(count_positives_sum_negatives([-1]),[0,-1])
Test.assert_equals(count_positives_sum_negatives([0,0,0,0,0,0,0,0,0]),[0,0])
Test.assert_equals(count_positives_sum_negatives([]),[])
My code:
def count_positives_sum_negatives(arr):
positive_count = 0
negative_sum = 0
for n in arr:
if n > 0:
positive_count += 1
elif n < 0:
negative_sum += n
return [positive_count, negative_sum]
It appears that you haven't handled the degenerate case, where there is nothing report because of an empty input list. Add a line to the top of your function:
if len(arr) == 0
return []
The following procedure needs to applied if a test fails:
Narrow down the tests to the one that is failing
Look at the input and expected output of that test
Change the code so that it matches the desired ouput
Check the impact on other tests by running all tests again

How do I make this into a for loop?

So basically I am trying to replace this:
board = {
0:[0, 1, 2, 9, 10, 11, 18, 19, 20],
1:[3, 4, 5, 12, 13, 14, 21, 22, 23],
2:[6, 7, 8, 15, 16, 17, 24, 25, 26]}
with a for loop that will automatically create it. Sorry if this seems obvious, but I'm a bit of a noob and I'm having a lot of trouble with this.
It looks like you're generating the first 27 integers (starting at 0) and then grouping them. Let's write it like that.
def group_by_threes(n=27, group_count=3):
# The end result will be a dict of lists. The number of lists
# is determined by `group_count`.
result = {}
for x in range(group_count):
result[x] = []
# Since we're using subgroups of 3, we iterate by threes:
for x in range(n // 3):
base = 3 * x
result[x % 3] += [base, base + 1, base + 2]
# And give back the answer!
return result
This code could be made better by making the size of groups (three in this case) an argument, but I'll leave that as an exercise to the reader. ;)
The advantage of this method is that it's much more modular and adaptable than just writing a one-off method that generates the exact list you're looking for. After all, if you only wanted to ever generate that one list you showed, then it'd probably be better to hardcode it!
def create_list(x):
a = [x,x+1,x+2,x+9,x+9+1,x+9+2,x+18,x+18+1,x+18+2]
return a
output = {}
for i in range(3):
output[i*3] = create_list(i*3)
print output
please try this you get desired output
def create_list(x):
res = []
for i in xrange(0,3):
for j in xrange(0,3):
res.append(3*x+ 9*i + j)
return res
dictionary={}
for i in xrange(0,3):
dictionary[i]=create_list(i)
print dictionary
Result:
{0: [0, 1, 2, 9, 10, 11, 18, 19, 20], 1: [3, 4, 5, 12, 13, 14, 21, 22, 23], 2: [6, 7, 8, 15, 16, 17, 24, 25, 26]}

efficiently converting a bijection to cycle notation

Consider the following problem. Given is an array P of length n representing a bijection. That is element 0 <= i < n is mapped to P[i].
Given that P is a permutation, it can be written in cycle notation as described for example here.
For example if
P = [16, 3, 10, 6, 5, 9, 1, 19, 13, 14, 7, 0, 2, 8, 12, 4, 17, 15, 11, 18]
then the result in cycle notation would be
[(16, 17, 15, 4, 5, 9, 14, 12, 2, 10, 7, 19, 18, 11, 0), (3, 6, 1), (13, 8)]
Following is a Python method accomplishing this
def toCycle(p):
covered = cur = 0
perm = []
n = len(p)
done = [0]*n
while covered < n:
while cur < n and done[cur] == -1:
cur+=1
cycle = [p[cur]]
sec = p[p[cur]]
done[p[cur]] = -1
done[cur] = -1
covered+=1
while sec != cycle[0]:
cycle.append(sec)
done[sec] = -1
sec = p[sec]
covered+=1
perm+=[tuple(cycle)]
return perm
The algorithm clearly runs in linear time (each element of done/p is accessed a constant number of times) and hence there is not much that can be done asymptotically.
As I have to use this method on a large number of large permutations I was wondering
Can you make it faster? Do you have any suggestions for performance
improvement?
def cycling(p, start, done):
while not done[e]:
done[e] = True
e = p[e]
yield e
def toCycle(p):
done = [False]*len(p)
cycles = [tuple(cycling(p, 0, done))]
while not all(done):
start = done.index(False)
cycles.append(tuple(cycling(p, start, done)))
return cycles
With your example my code runs about 30% faster than yours.

Generating a list of evenly distributed numbers from range

I have seen this question been asked before but I'm looking for an answer with a twist:
Consider I have a range like 1-100 and i want to generate a list, with a specific stepsize like: numbers(1,100,5). This would return [1,25,50,75,100]. However, I would like it to return [1,100,50,25,75] or [1,100,50,75,25] Another example would be numbers(1,10,10) which would give me something similar to [1,10,5,2,7,3,8,4,9].
Is this even possible to do? The reason for this would be to be able to render image sequences without going from frame 1 to frame 2 to frame 3 and so forth. Instead I want to render the first frame, the last frame, the middle frame, the middle of the middle frame until all frames are accounted for.
Your question is kind of underspecified, but this should help you get started.
def numbers(first, last, count):
nums = [first, last]
width = last - first
while len(nums) < count:
width /= 2
stepper = first
while stepper < last:
rounded = round(stepper)
if rounded not in nums:
nums.append(rounded)
if len(nums) == count:
break
stepper += width
return nums
Then:
>>> numbers(0, 100, 5)
[0, 100, 50, 25, 75]
>>> numbers(0, 10, 10)
[0, 10, 5, 2, 8, 1, 4, 6, 9, 3, 7]
>>> numbers(0, 50, 50)
[0, 50, 25, 12, 38, 6, 19, 31, 44, 3, 9, 16, 22, 28, 34, 41, 47, 2, 5, 8, 11, 14, 17, 20, 23, 27, 30, 33, 36, 39, 42, 45, 48, 1, 4, 7, 10, 13, 15, 18, 21, 24, 26, 29, 32, 35, 37, 40, 43, 46]
The basic algorithm is as follows:
Start with a list of nums containing the two endpoints
Initialize width to the distance between the two endpoints
Then, loop:
Halve width
Step through first, first+width, first+2*width, ..., last-width, last, and add whichever among those are not already in nums to nums (so, for numbers(0, 100, 5), the first loop iteration will try 0, 50, and 100, and only add 50 since that wasn't already present; the second iteration will try 0, 25, 50, 75, and 100, and only add 25 and 75).
If we have enough numbers in nums, we're done
Return nums
Ok, so your desired frames are kind of wierd, especially as the elements in the first example aren't evenly ranged, eg. 100-75=25, 75-50=25, 50-25=25 , 25-1=24.
But, if we assume that you always want the start and end value in the frame and want the evenly spaced values pegged against the maximum value, we can do this:
def numbers(start,stop,step=1):
frame = [start]+range(y,stop,(start-stop)/(step-1))
return frame
The random module includes a shuffle() method that takes an array and shuffles it in-place.
Which means the function becomes:
from random import shuffle
def numbers(start,stop,step=1):
frame = [start]+range(y,stop,(start-stop)/(step-1))
shuffle(frame)
return frame
Giving us the following test runs:
>>> numbers(1,100,5)
[100, 50, 75, 1, 25]
>>> numbers(1,10,10)
[1, 3, 10, 9, 6, 5, 8, 4, 7, 2]
Original (but wrong) answer
The random module includes a shuffle() method that takes an array and shuffles it in-place.
For example:
from random import shuffle
def numbers(start,stop,step=1):
frame = range(start,stop,step)
shuffle(frame)
return frame
Then calling this function we get:
>>> numbers(1,100,25)
[1, 51, 26, 76]
>>> numbers(1,100,25)
[76, 26, 1, 51]
Note, that as per the range() function, the step value is repeatedly added to start such that the final array is of the form [start, start+1*step, start+2*step ... start + n*step], where start+n*step is less than stop while start+(n+1)*step is greater than stop.

Categories

Resources