been trying to understand how to use dynamic programming on this page
https://www.topcoder.com/community/data-science/data-science-tutorials/dynamic-programming-from-novice-to-advanced/
Given a list of N coins, their values (V1, V2, … , VN), and the total sum S. Find the minimum number of coins the sum of which is S (we can use as many coins of one type as we want), or report that it’s not possible to select coins in such a way that they sum up to S.
Given coins with values 1, 3, and 5.
And the sum S is set to be 11.
Set Min[i] equal to Infinity for all of i
Min[0]=0
For i = 1 to S
For j = 0 to N - 1
If (Vj<=i AND Min[i-Vj]+1<Min[i])
Then Min[i]=Min[i-Vj]+1
Output Min[S]
I am confused as to why we are setting infinity for all of i.
even more confusing is when the sum is 1
If (Vj<=i AND Min[i-Vj]+1<Min[i])
wouldn't Min[1] be undefined? wouldn't the code fail here? and why are they adding +1??
or will it continue because is it infinite? Why are they using infinite here? And what is N-1 where did they get it from?
Overall I found their explanation very hard to follow.
This is hopefully a direct translation:
def dp_coin(S, coins):
# set all values to infinity in range S/sum needed
mn = [float("inf") for j in range(S+1)]
# takes 0 coins to sum 0
mn[0] = 0
# start at second index 1
for i in range(1, S+1):
for j in range(len(coins)):
if coins[j] <= i and mn[i-coins[j]]+1 < mn[i]:
mn[i] = mn[i-coins[j]] + 1
return mn[-1]
print(dp_coin(11, [1, 3, 5]))
3
If you print mn you will see:
[0, 1, 2, 1, 2, 1, 2, 3, 2, 3, 2, 3]
which is the same as the table:
Sum Min. nr. of coins Coin value added to a smaller sum to
obtain this sum (it is displayed in brackets)
0 0 -
1 1 1 (0)
2 2 1 (1)
3 1 3 (0)
4 2 1 (3)
5 1 5 (0)
6 2 3 (3)
7 3 1 (6)
8 2 3 (5)
9 3 1 (8)
10 2 5 (5)
11 3 1 (10)
S refers to the total sum required, coins[j] is equivalent to Vj, N was referring to coins.
The inner loop can be removed and simply iterate over coins:
def dp_coin(S, coins):
mn = [float("inf") for j in range(S+1)]
mn[0] = 0
for i in range(1, S+1):
for j in coins:
if j <= i and mn[i-j]+1 < mn[i]:
mn[i] = mn[i-j] + 1
return mn[-1]
Related
I am really struggling with this program. I would appreciate any kind of help.
For a natural number we say that it is strange if it is completely composed of digits 2 and 3. The user enters a natural number. The program prints the n-th strange number.
Numbers that are considered strange are 2, 3, 22, 23, 33...
n = int(input())
current_number = 1
counter_strange = 0
counter = 0
while counter_strange < n:
x = current_number
while x < n:
k = x % 10
if k != 2 or k != 3:
counter += 1
else:
break
if counter >= 1:
counter_strange += 1
current_number += 1
print(current_number-1)
Strange numbers come in blocks. A block of 2 1-digit numbers, followed by a block of 4 2-digit numbers, then 8 3-digit numbers. You can do the math and determine which block of k-digit numbers a given index n is, and how far into that block it lies. Convert that distance into a base-2 number, zero-filled to k digits, then replace 0 by 2 and 1 by 3. Convert the result back to an int:
from math import log2, ceil
def strange(n):
"""returns the nth strange number"""
#first determine number of digits:
k = ceil(log2(n+2)) - 1
#determine how far is in the block of strange k-digit numbers
d = n - (2**k - 1)
#convert to base 2, zfilling to k digits:
s = bin(d)[2:].zfill(k)
#swap 2,3 for 0,1:
s = s.replace('0','2').replace('1','3')
#finally:
return int(s)
for n in range(1,10): print(n,strange(n))
Output:
1 2
2 3
3 22
4 23
5 32
6 33
7 222
8 223
9 232
You can use a while loop with itertools.product in a generator function. Using a generator will allow you to create a stream from which you can access strange numbers on the fly:
import itertools
def strange():
c = 0
while True:
yield from map(''.join, itertools.product(*([['2', '3']]*(c:=c+1))))
s = strange()
for _ in range(10):
print(int(next(s)))
Output:
2
3
22
23
32
33
222
223
232
233
Say I have a undirected graph (can be cyclic or acyclic), where each node is asigned with an integer state. I want to find the path that:
goes through every node but only once
doesn't need to go through every edge
maximize the sum of the state changes of each move
As an example, I have a cyclic graph -5-4-5-7-2- (first 5 and last 2 are connected). If we start from the first 5 and end at the last 2, the sum of the changes of each move will be -1 + 1 + 2 + (-5) = -3. The graph can be described by an adjacency matrix as follows:
import numpy as np
node_states = [5, 4, 5, 7, 2]
# Adjacency matrix
#5 4 5 7 2
am = np.array([[0,1,0,0,1], # 5
[1,0,1,0,0], # 4
[0,1,0,1,0], # 5
[0,0,1,0,1], # 7
[1,0,0,1,0]])# 2
The expected output is
max_delta_sum_path = [2, 5, 4, 5, 7]
where the path has the largest sum 3 + (-1) + 1 + 2 = 5
Anyone knows if there is any relatively fast algorithm that can automatically find this path?
replace each undirected link with two directed, costed links. e.g a link between nodes of state 5 and 7 would be replaced by two links with costs of +2 and -2.
add value to cost of every link that will make all link costs positive
find cost of most expensive link and subtract from every link cost
multiply every link cost by -1
apply travelling salesman algorithm
So, for your example:
0 -> 1 cost -1 converts to 6
0 -> 4 cost -3 converts to 8
1 -> 0 cost 1 converts to 4
1 -> 2 cost 1 converts to 4
2 -> 1 cost -1 converts to 6
2 -> 3 cost 2 converts to 3
3 -> 2 cost -2 converts to 7
3 -> 4 cost -5 converts to 10
4 -> 0 cost 3 converts to 2
4 -> 3 cost 5 converts to 0
I think this is what you're looking for:
import numpy as np
node_states = [5, 4, 5, 7, 2]
# Adjacency matrix
#5 4 5 7 2
am = np.array([[0,1,0,0,1], # 5
[1,0,1,0,0], # 4
[0,1,0,1,0], # 5
[0,0,1,0,1], # 7
[1,0,0,1,0]])# 2
for i in range(len(node_states)):
for j in range(len(node_states)):
if am[i][j] == 1:
am[i][j] = node_states[i] - node_states[j] # go through ever entry in every list, and if it is 1 replace it with the traversal cost
"""
am = [[ 0 1 0 0 3]
[-1 0 -1 0 0]
[ 0 1 0 -2 0]
[ 0 0 2 0 5]
[-3 0 0 -5 0]]
"""
from itertools import permutations
def largest_sum(node_states, am):
largest = None
largest_journey = None
traversal_list = list(permutations(range(len(node_states)), len(node_states))) # store all possible permutations of node_states indexes
for trav in traversal_list: # go through each permuatation
costs = [] # track the cost of each traversal
for i in range(len(trav)):
if i == 0: # there are one less traversals than nodes so we are ignoring the first node
continue
if am[trav[i]][trav[i-1]] == 0: # traversal cannot happen if the traversal has no adjacency
continue
costs.append(am[trav[i]][trav[i-1]]) # use the updated am matrix to get our costs, and store them here
if len(costs) == len(node_states) - 1: # if one less traversal was made than we have nodes, we know all nodes were visited
costs_sum = sum(costs) # sum the costs for our total of travel
if largest is None or largest < costs_sum: # only keep this total if it was bigger than our old total
largest = costs_sum # track the new total
largest_trav = list(map(lambda x: node_states[x], trav)) # change our array of indexes (trav) into an array of node values
return largest_trav # when the looping is done, return our total
out = largest_sum(node_states, am)
print(out)
Output:
[2, 5, 4, 5, 7]
In one move we can make it equal to the 2nd maximum element and have to make all elements equal to the minimum element.
My code is given below it works fine but I want to reduce its time complexity.
def No_Books(arr, n):
arr = sorted(arr)
steps = 0
while arr[0]!= arr[arr.index(max(arr))]:
max1 = max(arr)
count = arr.count(max1)
scnd_max = arr.index(max1)-1
arr[scnd_max+count] = arr[scnd_max]
steps += 1
return steps
n = int(input())
arr = [int(x) for x in input().split()]
print(No_Books(arr,n))
Output
5
4 5 5 2 4
6
Here minimum moves required is 6
I'm interpreting the question in the following way:
For each element in the array, there is one and only one operation you're allowed to perform, and that operation is to replace an index's value with the array's current second-largest element.
How many operations are necessary to make the entire array's values equal to the initial minimum value?
With the example input 4 5 5 2 4 needing to go through the following steps:
Array - step - comments
4 5 5 2 4 - 0 - start
4 4 5 2 4 - 1 - replace the first 5 with 4 (the second-largest value in the array)
4 4 4 2 4 - 2 - replace the second 5 with 4
2 4 4 2 4 - 3 - replace the first 4 with 2
2 2 4 2 4 - 4
2 2 2 2 4 - 5
2 2 2 2 2 - 6
It took 6 steps, so the result is 6.
If that is correct, then I can change your quadratic solution (O(n^2), where n is the size of the array) to a quasilinear solution (O(n + mlogm) where n is the size of the array, and m is the number of unique values in the array), as follows.
The approach is to notice that each value needs to be dropped down to the next largest value for each unique value smaller than itself. So if we can track the count of each unique value, we can determine the number of steps without actually doing any array updates.
In pseudocode:
function determineSteps(array):
define map from integer to integer, defaulting to 0
for each value in array: // Linear in N
map(value)++
sort map by key, descending // M log M
// largerCount is the number of elements larger than the current second-largest value
define largerCount, assign 0 to largerCount
// stepCount is the number of steps required
define stepCount, assign 0 to stepCount
for each key in map except the last: // Linear in M
largerCount = largerCount + map(key)
stepCount = stepCount + largerCount
return stepCount
On your example input:
4 5 5 2 4
Create map { 4: 2, 5: 2, 2: 1 }
Sort map by key, descending: { 5: 2, 4: 2, 2: 1 }
stepCount = 0
largerCount = 0
Examine key = 5, map(key) = 2
largerCount = 0 + 2 = 2
stepCount = 0 + 2 = 2
Examine key = 4, map(key) = 2
largerCount = 2 + 2 = 4
stepCount = 2 + 4 = 6
return 6
This question already has answers here:
Last digit of power list
(2 answers)
Closed 4 years ago.
I need to find the unit digit of x1 ^ (x2 ^ (x3 ^ (... ^ xn))) from integers passed into the function as a list.
For example the input [3, 4, 2] would return 1 because 3 ^ (4 ^ 2) = 3 ^ 16 = 43046721 the last digit of which is 1.
The function needs to be efficient as possible because obviously trying to calculate 767456 ^ 981242 is not very quick.
I have tried a few methods but I think the best way to solve this is using sequences. For example any number ending in a 1, when raised to a power, will always end in 1. For 2, the resulting number will end in either 2, 4, 6 or 8.
If a number is raised to a power, the last digit of the resulting number will follow a pattern based on the last digit of the exponent:
1: Sequence is 1
2: Sequence is 2, 4, 8, 6
3: Sequence is 3, 9, 7, 1
4: Sequence is 4, 6
5: Sequence is 5
6: Sequence is 6
7: Sequence is 7, 9, 3, 1
8: Sequence is 8, 4, 2, 6
9: Sequence is 9, 1
0: Sequence is 0
I think the easiest way to calculate the overall last digit is to work backwards through the list and calculate the last digit of each calculation one at a time until I get back to the start but I am not sure how to do this?
If anyone could help or suggest another method that is equally or more efficient than that would be appreciated.
I have this code so far but it does not work for very large numbers
def last_digit(lst):
if lst == []:
return 1
total = lst[len(lst)-2] ** lst[len(lst)-1]
for n in reversed(range(len(lst)-2)):
total = pow(lst[n], total)
return total%10
Edit: 0 ^ 0 should be assumed to be 1
x^n = x^(n%4) because the last digit always has a period of 4.
x ^2 ^3 ^4 ^5
1 1 1 1 1
2 4 8 6 2
3 9 7 1 3
4 6 4 6 4
5 5 5 5 5
6 6 6 6 6
7 9 3 1 7
8 4 2 6 8
9 1 9 1 9
As you can see, all 9 digits have a period of 4 so we can use %4 to make calculations easier.
There's also a pattern if we do this %4.
x ^0 ^1 ^2 ^3 ^4 ^5 ^6 ^7 ^8 ^9
1 1 1 1 1 1 1 1 1 1 1
2 1 2 0 0 0 0 0 0 0 0
3 1 3 1 3 1 3 1 3 1 3
4 1 0 0 0 0 0 0 0 0 0
5 1 1 1 1 1 1 1 1 1 1 (all %4)
6 1 2 0 0 0 0 0 0 0 0
7 1 3 1 3 1 3 1 3 1 3
8 1 0 0 0 0 0 0 0 0 0
9 1 1 1 1 1 1 1 1 1 1
As shown, there is a pattern for each x when n>1. Therefore, you can see that (x^n)%4 = (x^(n+4k))%4 when n>1. We can then prevent the issues that arises from n=0 and n=1 by adding 4 to n. This is because, if (x^n)%4 = (x^(n+4k))%4, then (x^n)%4 = (x^(n%4+4))%4 as well.
powers = [3, 9, 7, 1]
lastDigit = 1
for i in range(len(powers) - 1, -1, -1):
if lastDigit == 0:
lastDigit = 1
elif lastDigit == 1:
lastDigit = powers[i]
else:
lastDigit = powers[i]**(lastDigit%4+4)
print(lastDigit%10)
This is more math than programming. Notice that all the sequences you listed has length either 1, 2, or 4. More precisely, x^4 always ends with either 0, 1, 5, 6, as does x^(4k). So if you know x^(m mod 4) mod 10, you know x^m mod 10.
Now, to compute x2^(x3^(...^xn)) mod 4. The story is very similar, x^2 mod 4 is ether 0 if x=2k or 1 if x=2k+1 (why?). So
is 0 if x2 == 0
is 1 if x2 > 0 and x3 == 0
if x2 is even, then it is either 2 or 0 with 2 occurs only when x2 mod 4 == 2 and (x3==1 or (any x4,...xn == 0) ).
if x2 is odd, then x2^2 mod 4 == 1, so we get 1 if x3 is even else x2 mod 4.
Enough math, let's talk coding. There might be corner cases that I haven't cover, but it's should work for most cases.
def last_digit(lst):
if len(lst) == 0:
return 1
x = lst[0] % 10
if len(lst) == 1:
return x
# these number never change
if x in [0,1,5,6]:
return x
# now we care for x[1] ^ 4:
x1 = x[1] % 4
# only x[0] and x[1]
if len(lst) == 2 or x1==0:
return x[0] ** x1 % 10
# now that x[2] comes to the picture
if x1 % 2: # == 1
x1_pow_x2 = x1 if (x[2]%2) else 1
else:
x1_pow_x2 = 2 if (x1==2 and x[2]%2 == 1) else 0
# we almost done:
ret = x ** x1_pow_x2 % 10
# now, there's a catch here, if x[1]^(x[2]^...^x[n-1]) >= 4,
# we need to multiply ret with the last digit of x ** 4
if x[1] >=4 or (x[1] > 1 and x[2] > 1):
ret = (ret * x**4) % 10
return ret
Working off of your sequences idea and fleshing it out, you'd want to create a dictionary that can map all relevant sequences.
mapping = {}
for i in range(1, 10):
mapping[i] = [i]
last_digit = i
while True:
last_digit *= i
last_digit = last_digit%10
if last_digit in mapping[i]:
break
else:
mapping[i].append(last_digit)
print(mapping)
This produces Output: mapping
{1: [1],
2: [2, 4, 8, 6],
3: [3, 9, 7, 1],
4: [4, 6],
5: [5],
6: [6],
7: [7, 9, 3, 1],
8: [8, 4, 2, 6],
9: [9, 1]}
Now the real logic can start, The key takeaway is that the pattern repeats itself after the sequence is completed. So, it does not matter how big the power is, if you just use a modulo and figure out which position of the sequence it should occupy.
def last_digit_func(lst, mapping):
if lst == []: #taken from OP
return 1
last_digit = lst[0] % 10
if 0 in lst[1:]: #edge case 0 as a power
return 1
if last_digit == 0: #edge case 0 at start
return last_digit
for current_power in lst[1:]:
if len(mapping[last_digit]) == 1:
return last_digit
ind = current_power % len(mapping[last_digit])
ind -= 1 #zero indexing, but powers start from 1.
last_digit = mapping[last_digit][ind]
return last_digit
test1 = [3, 4, 2]
print(last_digit_func(test1, mapping)) #prints 1
I verified this by calculating the powers in python.
test2 = [767456 , 981242]
print(last_digit_func(test2, mapping)) #prints 6
And i tried to verify this by running it in python....i now have instant regrets and my program is still trying to solve it out. Oh well :)
I've tried to create an algorithm for finding all paths of length 2, but it doesn't seem to work properly:
input_split = input().split(' ')
node_count = int(input_split[0])
input_count = int(input_split[1])
items = np.zeros((node_count, node_count), dtype=np.int32) # matrix of adjacency
for j in range(input_count):
split = input().split(' ')
x = int(split[0]) - 1 # convert 1 based coordinates to 0 based
y = int(split[1]) - 1
items[x][y] = 1
items[y][x] = 1
result = np.linalg.matrix_power(items, 2)
result_sum = int(np.sum(result) / 2) # reverse paths are counted only once
print(result_sum)
Sample input:
6 7
1 2
2 3
3 1
2 4
4 5
5 6
6 2
The result should be 11, but it prints 18.
You're on the right track when calculating the square of the adjacency matrix. After the exponentiation you would get result matrix that looks like this:
[[2 1 1 1 0 1]
[1 4 1 0 2 0]
[1 1 2 1 0 1]
[1 0 1 2 0 2]
[0 2 0 0 2 0]
[1 0 1 2 0 2]]
First you need to exclude all diagonal entries from this matrix, because those denote walks that are not paths, as their starting and ending node is the same. Note that for length 2 that is the only way how nodes can be repeating.
The other entries need to be counted only once, because of symmetry. So only look at the upper right triangle of the matrix.
One way to do it is:
result_sum = 0
for i in range(input_count - 1):
for j in range(i + 1, input_count - 1):
result_sum += result[i][j]
print(result_sum) # prints 11
More Pythonic way, one-liner using numpy.trace():
result_sum = (np.sum(result) - np.trace(result)) // 2
You are calculating walks, which would include walks 6-7-6 (which is not a P2)
this discussion might help:
https://math.stackexchange.com/questions/1890620/finding-path-lengths-by-the-power-of-adjacency-matrix-of-an-undirected-graph