I'm implementing the Select Algorithm (a.k.a. Deterministic Select). I've got it working for small arrays/lists but when my array size gets above 26 it breaks with the following error: "RuntimeError: maximum recursion depth exceeded". For arrays size 25 and below there is no problem.
My ultimate goal is to have it run for arrays of size 500 and do many iterations. The iterations are not an issue. I have already researched StackOverflow and have seen article: Python implementation of "median of medians" algorithm among many others. I had a hunch that duplicates in my random generated array may have been causing a problem but that doesn't seem to be it.
Here's my code:
import math
import random
# Insertion Sort Khan Academy video: https://www.youtube.com/watch?v=6pyeMmJTefg&list=PL36E7A2B75028A3D6&index=22
def insertion_sort(A): # Sorting it in place
for index in range(1, len(A)):# range is up to but not including len(A)
value = A[index]
i = index - 1 # index of the item that is directly to the left
while i >= 0:
if value < A[i]:
A[i + 1] = A[i]
A[i] = value
i = i - 1
else:
break
timeslo = 0 # I think that this is a global variable
def partition(A, p):
global timeslo
hi = [] #hold things larger than our pivot
lo = [] # " " smaller " " "
for x in A: # walk through all the elements in the Array A.
if x <p:
lo = lo + [x]
timeslo = timeslo + 1 #keep track no. of comparisons
else:
hi = hi + [x]
return lo,hi,timeslo
def get_chunks(Acopy, n):
# Declare some empty lists to hold our chunks
chunk = []
chunks = []
# Step through the array n element at a time
for x in range(0, len(Acopy), n): # stepping by size n starting at the beginning
# of the array
chunk = Acopy[x:x+n] # Extract 5 elements
# sort chunk and find its median
insertion_sort(chunk) # in place sort of chunk of size 5
# get the median ... (i.e. the middle element)
# Add them to list
mindex = (len(chunk)-1)/2 # pick middle index each time
chunks.append(chunk[mindex])
# chunks.append(chunk) # assuming subarrays are size 5 and we want the middle
# this caused some trouble because not all subarrays were size 5
# index which is 2.
return chunks
def Select(A, k):
if (len(A) == 1): # if the array is size 1 then just return the one and only element
return A[0]
elif (len(A) <= 5): # if length is 5 or less, sort it and return the kth smallest element
insertion_sort(A)
return A[k-1]
else:
M = get_chunks(A, 5) # this will give you the array of medians,,, don't sort it....WHY ???
m = len(M) # m is the size of the array of Medians M.
x = Select(M, m/2)# m/2 is the same as len(A)/10 FYI
lo, hi, timeslo = partition(A, x)
rank = len(lo) + 1
if rank == k: # we're in the middle -- we're done
return x, timeslo # return the value of the kth smallest element
elif k < rank:
return Select(lo, k) # ???????????????
else:
return Select(hi, k-rank)
################### TROUBLESHOOTING ################################
# Works with arrays of size 25 and 5000 iterations
# Doesn't work with " 26 and 5000 "
#
# arrays of size 26 and 20 iterations breaks it ?????????????????
# A = []
Total = 0
n = input('What size of array of random #s do you want?: ')
N = input('number of iterations: ')
# n = 26
# N = 1
for x in range(0, N):
A = random.sample(range(1,1000), n) # make an array or list of size n
result = Select(A, 2) #p is the median of the medians, 2 means the 3rd smallest element
Total = Total + timeslo # the total number of comparisons made
print("the result is"), result
print("timeslo = "), timeslo
print("# of comparisons = "), Total
# A = [7, 1, 3, 5, 9, 2, 83, 8, 4, 13, 17, 21, 16, 11, 77, 33, 55, 44, 66, 88, 111, 222]
# result = Select(A, 2)
# print("Result = "), result
Any help would be appreciated.
Change this line
return x, timeslo # return the value of the kth smallest element
into
return x # return the value of the kth smallest element
You can get timeslo by printing it in the end. Returning x with timeslo is not correct, because it will be used in the partition(A, p) to split array, where the parameter p should be the median number from previous statement x = Select(M, m/2)
Related
I am solving some python problems and been bashing my head to solve this task. I need to generate matrix N x N filled with natural numbers where:
Average of numbers in each row is natural number which is present in
that row
Average of numbers in each column is natural number which is
present in that column
All numbers in matrix are different
So far I have generated matrix and tried with random.choice but I don't see a way to handle it so that both row and columns have average present in that row/column.
Here is my code:
import random
used_numbers = []
matrix = []
matrix_done = False
rows_generated = 0
n = int(input("please enter number for nxn matrix: "))
start = 1
max_n = n
while(matrix_done is False):
row = random.sample(range(start, max_n + 1), n)
average = sum(row) / len(row)
print("row", row, average)
start = max_n + 1
max_n = max_n + n
print("start***", start, max_n)
matrix.append(row)
if(average > 0 and average.is_integer()):
print("is integer true")
rows_generated += 1
if(rows_generated == n):
matrix_done = True
for row in matrix:
print(row)
I created the code below using the following principle:
If n is even: it is just an arithmetic progression
If n is odd: we have to compensate the last element of each line, column and the very last of them to ensure the average is inside the matrix
I defined a the start at 10 so it makes easy to check results. But you can make it start in any given random number if you wish.
import random
import numpy as np
# Get user inputs
n = int(input("please enter number for nxn matrix: "))
s = 10 # First element in the matrix. Can use random.randint(1, 100)
r = 2 # Arithmetic progression ratio
c = n*(r + 1) # How much change from one line to another. Use it to avoid having the same numbers
# Now we have to scenarios: the calculation is done differently if n is iven or odd
if (n % 2 == 0):
elements = []
for i in range(n):
for j in range(n):
e = s + r*j + c*i
if j == n - 1 and i == n - 1: # Last element of the matrix
e += c*n/2 + n
elif j == n - 1: # last element of the line
e += r*n/2
elif i == n - 1: # last element of the column
e += c*n/2
else:
pass
elements.append(e)
else:
# If the number is even, we just need to create an arithmetic progression and reshape it into a matrix
elements = [s + r*i for i in range(n*n)]
matrix = np.reshape(elements, (n, n))
print(matrix)
I am trying to create a function where:
The output list is generated from random numbers from the input list
The output list is a specified length and adds to a specified sum
ex. I specify that I want a list that is 4 in length and adds up to 10. random numbers are pulled from the input list until the criteria is satisfied.
I feel like I am approaching this problem all wrong trying to use recursion. Any help will be greatly appreciated!!!
EDIT: for more context on this problem.... Its going to be a random enemy generator.
The end goal input list will be coming from a column in a CSV called XP. (I plan to use pandas module). But this CSV will have a list of enemy names in the one column, XP in another, Health in another, etc. So the end goal is to be able to specify the total number of enemies and what the sum XP should be between those enemies and have the list generate with the appropriate information. For ex. 5 enemies with a total of 200 XP between them. The result is maybe -> Apprentice Wizard(50 xp), Apprentice Wizard(50 xp), Grung(50), Xvart(25 xp), Xvart(25 xp). The output list will actually need to include all of the row information for the selected items. And it is totally fine to have duplicated in the output as seen in this example. That will actually make more sense in the narrative of the game that this is for.
The csv --> https://docs.google.com/spreadsheets/d/1PjnN00bikJfY7mO3xt4nV5Ua1yOIsh8DycGqed6hWD8/edit?usp=sharing
import random
from random import *
lis = [1,2,3,4,5,6,7,8,9,10]
output = []
def query (total, numReturns, myList, counter):
random_index = randrange(len(myList)-1)
i = myList[random_index]
h = myList[i]
# if the problem hasn't been solved yet...
if len(output) != numReturns and sum(output) != total:
print(output)
# if the length of the list is 0 (if we just started), then go ahead and add h to the output
if len(output) == 0 and sum(output) + h != total:
output.append(h)
query (total, numReturns, myList, counter)
#if the length of the output is greater than 0
if len(output) > 0:
# if the length plus 1 is less than or equal to the number numReturns
if len(output) +1 <= numReturns:
print(output)
#if the sum of list plus h is greater than the total..then h is too big. We need to try another number
if sum(output) + h > total:
# start counter
for i in myList:# try all numbers in myList...
print(output)
print ("counter is ", counter, " and i is", i)
counter += 1
print(counter)
if sum(output) + i == total:
output.append(i)
counter = 0
break
if sum(output) + i != total:
pass
if counter == len(myList):
del(output[-1]) #delete last item in list
print(output)
counter = 0 # reset the counter
else:
pass
#if the sum of list plus h is less than the total
if sum(output) + h < total:
output.append(h) # add h to the list
print(output)
query (total, numReturns, myList, counter)
if len(output) == numReturns and sum(output) == total:
print(output, 'It worked')
else:
print ("it did not work")
query(10, 4, lis, 0)
I guess that it would be better to get first all n-size combinations of given array which adds to specified number, and then randomly select one of them. Random selecting and checking if sum is equal to specified value, in pessimistic scenario, can last indefinitely.
from itertools import combinations as comb
from random import randint
x = [1,1,2,4,3,1,5,2,6]
def query(arr, total, size):
combs = [c for c in list(comb(arr, size)) if sum(c)==total]
return combs[randint(0, len(combs))]
#example 4-item array with items from x, which adds to 10
print(query(x, 10, 4))
If the numbers in your input list are consecutive numbers, then this is equivalent to the problem of choosing a uniform random output list of N integers in the range [min, max], where the output list is ordered randomly and min and max are the smallest and largest number in the input list. The Python code below shows how this can be solved. It has the following advantages:
It does not use rejection sampling.
It chooses uniformly at random from among all combinations that meet the requirements.
It's based on an algorithm by John McClane, which he posted as an answer to another question. I describe the algorithm in another answer.
import random # Or secrets
def _getSolTable(n, mn, mx, sum):
t = [[0 for i in range(sum + 1)] for j in range(n + 1)]
t[0][0] = 1
for i in range(1, n + 1):
for j in range(0, sum + 1):
jm = max(j - (mx - mn), 0)
v = 0
for k in range(jm, j + 1):
v += t[i - 1][k]
t[i][j] = v
return t
def intsInRangeWithSum(numSamples, numPerSample, mn, mx, sum):
""" Generates one or more combinations of
'numPerSample' numbers each, where each
combination's numbers sum to 'sum' and are listed
in any order, and each
number is in the interval '[mn, mx]'.
The combinations are chosen uniformly at random.
'mn', 'mx', and
'sum' may not be negative. Returns an empty
list if 'numSamples' is zero.
The algorithm is thanks to a _Stack Overflow_
answer (`questions/61393463`) by John McClane.
Raises an error if there is no solution for the given
parameters. """
adjsum = sum - numPerSample * mn
# Min, max, sum negative
if mn < 0 or mx < 0 or sum < 0:
raise ValueError
# No solution
if numPerSample * mx < sum:
raise ValueError
if numPerSample * mn > sum:
raise ValueError
if numSamples == 0:
return []
# One solution
if numPerSample * mx == sum:
return [[mx for i in range(numPerSample)] for i in range(numSamples)]
if numPerSample * mn == sum:
return [[mn for i in range(numPerSample)] for i in range(numSamples)]
samples = [None for i in range(numSamples)]
table = _getSolTable(numPerSample, mn, mx, adjsum)
for sample in range(numSamples):
s = adjsum
ret = [0 for i in range(numPerSample)]
for ib in range(numPerSample):
i = numPerSample - 1 - ib
# Or secrets.randbelow(table[i + 1][s])
v = random.randint(0, table[i + 1][s] - 1)
r = mn
v -= table[i][s]
while v >= 0:
s -= 1
r += 1
v -= table[i][s]
ret[i] = r
samples[sample] = ret
return samples
Example:
weights=intsInRangeWithSum(
# One sample
1,
# Count of numbers per sample
4,
# Range of the random numbers
1, 5,
# Sum of the numbers
10)
# Divide by 100 to get weights that sum to 1
weights=[x/20.0 for x in weights[0]]
the python code is:
def max_heapify(i, arr, n):
l = 2*i
r = 2*i+1
largest = i
if (2*i <= n-1 and arr[l] > arr[i]):
largest = l
if (2*i+1 <= n-1 and arr[r] > arr[largest]):
largest = r
if largest != i:
temp = arr[largest]
arr[largest] = arr[i]
arr[i] = temp
max_heapify(largest, arr, n)
return arr
arr=[16,4,10,14,7,9,3,2,8,1]
n=len(arr)
#max_heapify(i,arr,n)
for i in range(n//2):
max_heapify(n//2-1-i,arr,n)
Try this
Python Program for Heap Sort
Heap sort is a comparison based sorting technique based on Binary Heap data structure. It is similar to selection sort where we first find the maximum element and place the maximum element at the end. We repeat the same process for remaining element.
Python program for implementation of heap Sort
To heapify subtree rooted at index i.
n is size of heap
def heapify(arr, n, i):
largest = i # Initialize largest as root
l = 2 * i + 1 # left = 2*i + 1
r = 2 * i + 2 # right = 2*i + 2
# See if left child of root exists and is
# greater than root
if l < n and arr[i] < arr[l]:
largest = l
# See if right child of root exists and is
# greater than root
if r < n and arr[largest] < arr[r]:
largest = r
# Change root, if needed
if largest != i:
arr[i],arr[largest] = arr[largest],arr[i] # swap
# Heapify the root.
heapify(arr, n, largest)
The main function to sort an array of given size
def heapSort(arr):
n = len(arr)
# Build a maxheap.
for i in range(n, -1, -1):
heapify(arr, n, i)
# One by one extract elements
for i in range(n-1, 0, -1):
arr[i], arr[0] = arr[0], arr[i] # swap
heapify(arr, i, 0)
Driver code to test above
arr = [ 12, 11, 13, 5, 6, 7]
heapSort(arr)
n = len(arr)
print ("Sorted array is")
for i in range(n):
print ("%d" %arr[i]),
This code is contributed by Mohit Kumra
Output:
Sorted array is
5 6 7 11 12 13
How to write a recursive function that generates a list of binary of length n with a specified number of 1s?
Here's a code that generates recursively a list of binarys; without a specified number of 1s:
def generateAllBinaryStrings(n, arr, i):
if i == n:
printTheArray(arr, n)
return
# First assign "0" at ith position
# and try for all other permutations
# for remaining positions
arr[i] = 0
generateAllBinaryStrings(n, arr, i + 1)
# And then assign "1" at ith position
# and try for all other permutations
# for remaining positions
arr[i] = 1
generateAllBinaryStrings(n, arr, i + 1)
Taken from geeksforgeeks
You could do it like this:
def binaryStrings(n, ones):
if n < ones: # impossible
return []
if n == ones:
return ["1" * ones]
if ones == 0:
return ["0" * n]
a = binaryStrings(n-1, ones)
b = binaryStrings(n-1, ones-1)
return ["0"+s for s in a] + ["1"+s for s in b]
Example call to get all 6-digit binary numbers which have exactly 4 1-digits:
print(binaryStrings(6,4))
You have to generate all possible sequences with adding a 0 or a 1 at each position. Total possible sequences = 2^MAX. Keep track of the number of 1s in the current sequence so far to break.
# Generate all binary numbers with exactly "n" 1s
# Max digits in the binary number = MAX
def binary(n):
MAX = 5
all_solutions = []
def solve(current, remaining_ones):
if len(current) > MAX:
return
if remaining_ones == 0:
all_solutions.append(current+"0"*(MAX-len(current)))
return
solve(current+"1", remaining_ones - 1)
solve(current+"0", remaining_ones)
solve("", n)
return all_solutions
print(binary(2))
# ['11000', '10100', '10010', '10001', '01100', '01010', '01001', '00110', '00101', '00011']
Not sure if it's the best title. The explanation of what the program is suposed to do is below, my version only works with the first example but it doesn't work in the second when you get for example 1 1 3 1 1 2 because i can't figure out a good way to handle so much variations especially if K is bigger than 3 and the limit is 50. My version:
N, K, M = map(int, input().split())
niz = list(map(int, input().split()))
nizk = list(range(1, K+1))
izlazi = []
for r in range(0, M):
operacija = list(map(int, input().split()))
index = 0
if operacija[0] == 2:
nizkk = []
for z in range(0, len(nizk)):
if nizk[z] in niz:
continue
else:
izlazi.append(-1)
break
for p in range(0, N):
if niz[p] not in nizkk:
nizkk.append(niz[p])
nizkk.sort()
if nizkk == nizk:
index = p
izlazi.append(index+1)
break
else:
continue
else:
index, repl = map(int, operacija[1:])
niz[index - 1] = repl
print(izlazi)
In the first line of the input there should be N, K, M (1 <= N, M <= 100k, 1 <= K <= 50, you don't need to actually check this the numbers that are tested will always be in those ranges). In the second line of input you put a list of numbers which are the lenght of N you entered earlier. M is the number of operations you will do in the following lines of input. There can be 2 operations. If you enter 1 p v(p = index of number you want to replace, v the number you replace it with) or if you enter 2 it needs to find the shortest array of numbers defined by range(1, K+1) in the list of numbers you entered in line 2 and possibly changed with operation 1. If it doesn't exist it should output -1 if it does it should output lenght of numbers in the array you look in(numbers can be like 2, 1, 3 if you're looking for 1, 2, 3, also if you're looking for 1, 2, 3 etc and you have 2, 1, 1, 3 as the shortest one that is the solution and it's lenght is 4). Also the replacement operation doesnt count from 0 but from 1. So watch out when managing lists.
These are the examples you can input in the program ulaz = input, izlaz = ouput:
I have the following idea:
Min length sequence either starts from first element or does not contain first element and hence equals to min length of the same sequence without first element.
So we have recursion here.
For sequence [1,1,3,2,1,1] and [1,2,3] we will have:
Min length from start element [1,1,3,2,1,1] is 4
Min length from start element __[1,3,2,1,1] is 3
Min length from start element ____[3,2,1,1] is 3
Min length from start element ______[2,1,1] is -1
Can stop here.
Result is minimum for [4,3,3] = 3
You have already implemented the part for min length, if it starts from the first element. Need now extract it as a function and create a recursive function.
Some metacode:
function GetMinLength(seq)
{
minLengthFromFirstElement = GetMinLenthFromFirstElement(seq)
minLengthFromRest = GetMinLength(seq[1:]) //recusive call
return Min(minLengthFromFirstElement, minLengthFromRest )//-1 results should not count, add extra code to handle it
}
Unfortunately I don't know python, but I can provide code on F# in case you need it.
EDIT:
Try this:
N, K, M = map(int, input().split())
niz = list(map(int, input().split()))
nizk = list(range(1, K+1))
izlazi = []
def GetMinLengthStartingFromFirstElement(seq):
nizkk = []
for z in range(0, len(seq)):
if seq[z] in nizk:
continue
else:
return -1
for p in range(0, len(seq)):
if seq[p] not in nizkk:
nizkk.append(seq[p])
nizkk.sort()
if nizkk == nizk:
index = p
return index+1
else:
continue
return -1
def GetMinLength(seq):
if len(seq) == 0:
return -1
else:
curMinLength = GetMinLengthStartingFromFirstElement(seq)
if curMinLength == -1:
return -1
minLengthFromRest = GetMinLength(seq[1:])
if minLengthFromRest > -1:
return min(curMinLength,minLengthFromRest)
else:
return curMinLength;
for r in range(0, M):
operacija = list(map(int, input().split()))
index = 0
if operacija[0] == 2:
minLength = GetMinLength(niz)
izlazi.append(minLength)
else:
index, repl = map(int, operacija[1:])
niz[index - 1] = repl
print(izlazi)