Related
I'm creating a macro for an RPG, in Lua, in it I need to get the most sets with a stack of dices. to form a group the data must add up to the minimum of each group, and may exceed this minimum.
ex: 1, 2, 4, 5, 5, 6, 7, 10 w/ min = 10 will be: 6+4, 5+5, 7+1+2, 10.
I grouped the result of each dice into an array, and pulled out the data that can form groups on their own:
for i=#dice, 1, -1 do
table.sort(dice);
minimo = tonumber(minimum)
if dice[i] >= minimum then
stack.Total = stack.total+1;
table.insert(stack.dice, 1, math.floor(dice[i]))
table.remove(dice, i);
end;
end;
it doesn't have to be in Lua, just some mathematical formula will be of great help
Here's an efficient recursive solution. It likely doesn't scale as well as solving a mixed integer program would, but it's simple and doesn't require an external library. You could probably make it even faster by memoizing it, at the expense of a lot of memory.
The core idea is: form all possible groups that meet the minimum; for each such group, make the max number of groups out of the remaining rolls; take the best solution. The rest is optimization.
The first optimization is to loop over only some groups. Since we might as well put every roll in a group, the largest roll is in some group. To avoid looping over all permutations of the groups, enumerate the possibilities for that group only.
The second optimization is to stop searching if we find a provable optimum. Obviously we can't make more groups than the floor of the sum over the minimum. If we make this many, we can't improve.
The third optimization is to avoid enumerating duplicate groups. When we decrease i, we're considering groups that did not include the element at that position. To avoid duplicates, we skip i over the elements identical to the one that we just rejected.
In Python 3:
def all_groups(minimum, rolls, j):
roll = rolls[j]
if minimum <= roll:
yield [roll], rolls[:j]
else:
i = j - 1
while i >= 0:
for group, rest in all_groups(minimum - roll, rolls, i):
group.append(roll)
rest.extend(rolls[i + 1 : j])
yield group, rest
while i > 0 and rolls[i - 1] == rolls[i]:
i -= 1
i -= 1
def max_groups_helper(minimum, rolls, lower_bound=0):
upper_bound = sum(min(roll, minimum) for roll in rolls) // minimum
if upper_bound < lower_bound:
return None
if upper_bound <= 0:
return []
best = []
for group, rest in sorted(
all_groups(minimum, rolls, len(rolls) - 1),
key=lambda group_rest: sum(group_rest[0]),
):
candidate = max_groups_helper(minimum, rest, max(lower_bound - 1, len(best)))
if candidate is None:
continue
candidate.append(group)
best = candidate
if len(best) >= upper_bound:
break
return best
def max_groups(minimum, rolls):
assert minimum > 0
rolls = list(rolls)
return max_groups_helper(minimum, rolls, 0)
I'm practicing my coding chops after a long break and ran into this kata on CodeWars
With an input of numbers in an array, return the sums of its parts. So for example:
def parts_sums(ls)
sums = []
until ls.size == 0
sums << ls.inject(:+)
ls.shift
end
sums << 0
end
######### INPUT #######
parts_sums([0, 1, 3, 6, 10])
######### EXPECTED OUTPUT ######
[20, 20, 19, 16, 10, 0]
0 + 1 + 3 + 6 + 10 = 20
1 + 6 + 3 + 10 = 20
3 + 6 + 10 = 19
6 + 10 = 16
10 = 10
0 = 0
My solution solves the kata, however once I reach arrays of around 30,000+ my solution takes too long to solve.
So my question is to the community, how would I even attempt to make this go faster. I know that recursion is usually slow, and that for loops and its variants are usually sufficient to get the job done. What happens when that fails? What are some things to try to make my code above faster?
I'm looking for some advice and some examples if anyone has any. Appreciate the input. Thanks.
def parts_sums(ls)
ls.each_with_object([ls.sum]) { |n,arr| arr << arr.last - n }
end
parts_sums([0, 1, 3, 6, 10])
#=> [20, 20, 19, 16, 10, 0]
The issue with the code is that you are performing an inject on every iteration of your loop, which is unnecessarily slow.
You only need to sum the elements of the array once, outside of any loop. Once you have that sum, you can iterate through the elements of the array and perform a constant time subtraction from the current sum and push it into the sums array.
def part_sums(ls)
sum = ls.inject(:+)
sums = [sum]
ls.each do |val|
sum -= val
sums << sum
end
sums
end
There is also no need to shift, if you iterate through the array with the each iterator or keep a counter and use a while loop.
This version of the function runs much faster:
def parts_sums_2(ls)
sums = []
last_sum = 0
(ls.length - 1).downto(0).each do |i|
last_sum += ls[i]
sums.prepend last_sum
end
sums + [0]
end
The key here is going backwards through the array - starting with the smallest sum (only the last element). Each subsequent step moves one index towards the beginning, and adds that value to the previous sum.
Since the problem statement requires you to shift each step, your result must have the largest sums at the beginning, even though these are the last ones to be computed. This is why my code uses prepend rather than push.
This is O(N) time complexity instead of O(N^2), which is an order of magnitude difference.
With 100_000 inputs, your original function took 7.040443 seconds, while mine here took 0.000008 seconds
Also in general you should try to avoid mutating the input to your methods (as you were doing with shift).
I am trying to solve a problem from codility
"Even sums"
but am unable to do so. Here is the question below.
Even sums is a game for two players. Players are given a sequence of N positive integers and take turns alternately. In each turn, a player chooses a non-empty slice (a subsequence of consecutive elements) such that the sum of values in this slice is even, then removes the slice and concatenates the remaining parts of the sequence. The first player who is unable to make a legal move loses the game.
You play this game against your opponent and you want to know if you can win, assuming both you and your opponent play optimally. You move first.
Write a function:
string solution(vector< int>& A);
that, given a zero-indexed array A consisting of N integers, returns a string of format "X,Y" where X and Y are, respectively, the first and last positions (inclusive) of the slice that you should remove on your first move in order to win, assuming you have a winning strategy. If there is more than one such winning slice, the function should return the one with the smallest value of X. If there is more than one slice with the smallest value of X, the function should return the shortest. If you do not have a winning strategy, the function should return "NO SOLUTION".
For example, given the following array:
A[0] = 4 A[1] = 5 A[2] = 3 A[3] = 7 A[4] = 2
the function should return "1,2". After removing a slice from positions 1 to 2 (with an even sum of 5 + 3 = 8), the remaining array is [4, 7, 2]. Then the opponent will be able to remove the first element (of even sum 4) or the last element (of even sum 2). Afterwards you can make a move that leaves the array containing just [7], so your opponent will not have a legal move and will lose. One of possible games is shown on the following picture
Note that removing slice "2,3" (with an even sum of 3 + 7 = 10) is also a winning move, but slice "1,2" has a smaller value of X.
For the following array:
A[0] = 2 A[ 1 ] = 5 A[2] = 4
the function should return "NO SOLUTION", since there is no strategy that guarantees you a win.
Assume that:
N is an integer within the range [1..100,000]; each element of array A is an integer within the range [1..1,000,000,000]. Complexity:
expected worst-case time complexity is O(N); expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments). Elements of input arrays can be modified.
I have found a solution online in python.
def check(start, end):
if start>end:
res = 'NO SOLUTION'
else:
res = str(start) + ',' + str(end)
return res
def trans( strr ):
if strr =='NO SOLUTION':
return (-1, -1)
else:
a, b = strr.split(',')
return ( int(a), int(b) )
def solution(A):
# write your code in Python 2.7
odd_list = [ ind for ind in range(len(A)) if A[ind]%2==1 ]
if len(odd_list)%2==0:
return check(0, len(A)-1)
odd_list = [-1] + odd_list + [len(A)]
res_cand = []
# the numbers at the either end of A are even
count = odd_list[1]
second_count = len(A)-1-odd_list[-2]
first_count = odd_list[2]-odd_list[1]-1
if second_count >= count:
res_cand.append( trans(check( odd_list[1]+1, len(A)-1-count )))
if first_count >= count:
res_cand.append( trans(check( odd_list[1]+count+1, len(A)-1 )))
twosum = first_count + second_count
if second_count < count <= twosum:
res_cand.append( trans(check( odd_list[1]+(first_count-(count-second_count))+1, odd_list[-2] )))
###########################################
count = len(A)-1-odd_list[-2]
first_count = odd_list[1]
second_count = odd_list[-2]-odd_list[-3]-1
if first_count >= count:
res_cand.append( trans(check( count, odd_list[-2]-1 )))
if second_count >= count:
res_cand.append( trans(check( 0, odd_list[-2]-count-1)) )
twosum = first_count + second_count
if second_count < count <= twosum:
res_cand.append( trans(check( count-second_count, odd_list[-3])) )
res_cand = sorted( res_cand, key=lambda x: (-x[0],-x[1]) )
cur = (-1, -2)
for item in res_cand:
if item[0]!=-1:
cur = item
return check( cur[0], cur[1] )
This code works and I am unable to understand the code and flow of one function to the the other. However I don't understand the logic of the algorithm. How it has approached the problem and solved it. This might be a long task but can anybody please care enough to explain me the algorithm. Thanks in advance.
So far I have figured out that the number of odd numbers are crucial to find out the result. Especially the index of the first odd number and the last odd number is needed to calculate the important values.
Now I need to understand the logic behind the comparison such as "if first_count >= count" and if "second_count < count <= twosum".
Update:
Hey guys I found out the solution to my question and finally understood the logic of the algorithm.
The idea lies behind the symmetry of the array. We can never win the game if the array is symmetrical. Here symmetrical is defined as the array where there is only one odd in the middle and equal number of evens on the either side of that one odd.
If there are even number of odds we can directly win the game.
If there are odd number of odds we should always try to make the array symmetrical. That is what the algorithm is trying to do.
Now there are two cases to it. Either the last odd will remain or the first odd will remain. I will be happy to explain more if you guys didn't understand it. Thanks.
I am designing a media player and I want to store songs and play them in random order
a) which data structure I should you use to store songs?
b) how I will select the next song randomly to play in a way which prevents the same song being played until all songs are once played.
Any help will be appreciated.
1)In my view, you can take a map whose key will be the song number and value will be the actual media file.
2)Take a vector of the same size of the number of songs.
As songs keep on getting stored in the map, increment the counter 'cnt' like-
songs[cnt++] = Song1.mp3
Similarly for rest of the songs.
Assign a vector, lets say we name it as 'hash', and it is initialized with 0
3) Now create a new function searchSong which will choose a random value from 1 to cnt and check whether corresponding index in 'hash' is set or not ,if not then set that index with 1 so that if same random value comes again then same song is not played and call a playSong function as through map you can use the "value" regarding this random number, if 'hash' at this index is set you can simply call the searchSong function again and not play the same song.
Hope this helps.
You need to separate songs data structure from played/not-played data structure. Because you don't want to alter song data structure when one song is played.
Basic idea is picking random songId from playIds. playIds contains all ids of the songs, such that when song is played, it is put at the end of list. So first elements are not played, last elements in playIds are played. The reason of this is, we can pick random un-played song easily by randomInt(0, lastPlayIndex), you always get un-played song (no need to regenerate random again).
The other thing is, getSongList(). When consumer wants list of all songs, consumer needs to sort based on needs (asc/desc by song name...) and cache preferably.
val songs: Map<String, Song> // "id1" - (song1, id1)
val playIds: List<String> // [0, 1, 4, 2]
var lastPlayIndex = -1 // songIds after this index (including) are played
// O(1)
fun addSong(song: Song):
if (songs.containsKey(song.id)) return // duplicate
songs[song.id] = song
// add to end -> swap (lastAddIndex, lastPlayIndex)
playIds.add(song.id)
if (lastPlayIndex != -1):
swapPlayIndex(playIds.size-1, lastPlayIndex)
lastPlayIndex += 1
size += 1
// O(N)
fun removeSong(id: String):
val song = songs[id]
if (song == null) return // does not exist
songs.remove(id)
val removeIndex = playIds.indexOf(id) // O(N)
if (lastPlayIndex == -1 || removeIndex >= lastPlayIndex):
playIds.removeAt(removeIndex)
// last played song is removed, so lastPlayIndex is reset
if ((playIds.size - 1) < lastPlayIndex)
lastPlayIndex = -1
else:
playIds.removeAt(removeIndex)
lastPlayIndex -= 1
size -= 1
// O(1)
fun playRandomSong(): Song?
if (playIds.isEmpty()) return null // no song available
if (lastPlayIndex == 0) return null // all songs are played
val from = 0
val until = if (lastPlayIndex == -1) playIds.size else lastPlayIndex
val randomIndex = Random.nextInt(from, until)
val randomSongId = playIds[randomIndex]
if (lastPlayIndex != -1):
// there is played song before
swapPlayIndex(randomIndex, lastPlayIndex - 1)
lastPlayIndex -= 1
else:
// this is first played song
swapPlayIndex(randomIndex, playIds.size - 1)
lastPlayIndex = playIds.size - 1
return songs[randomSongId]
fun play(id: String): Song?
fun getList(ascName: Boolean): List<Song>
Use an array data structure and manipulate the positions of items.
If using C++ or numpy in Python, they have an inbuilt function called random permutation which can be used for randomizing a set of items.
C++
for (int i=1; i<10; ++i) myvector.push_back(i); // 1 2 3 4 5 6 7 8 9
// using built-in random generator:
std::random_shuffle ( myvector.begin(), myvector.end() );
Python
>>> np.random.permutation(10)
array([1, 7, 4, 3, 0, 9, 2, 5, 8, 6])
>>> np.random.permutation([1, 4, 9, 12, 15])
array([15, 1, 9, 4, 12])
And once the randomized permutation is played, we can ask for another permutation.
Probably what you are looking for is Fisher-Yates Shuffle algorithm.
To shuffle an array a of n elements (indices 0..n-1):
for i from n - 1 downto 1 do
j = random integer with 0 <= j <= i
exchange a[j] and a[i]
Explained in detail here - https://www.geeksforgeeks.org/generate-a-random-permutation-of-1-to-n/
I have written a poker simulator that calculates the probability to win in texas holdem by running a simulation of games (i.e. monte carlo simulation). Currently it runs around 10000 simulations in 10 seconds which in general is good enough. However, there are apps on the iPhone that are running around 100x faster. I'm wondering whether there's anything I can do to speed up the program significantly. I have already replaced strings with lists and found many ways to speed up the program by around 2-3 times. But what can I do to possibly speed it up 50x-100x? I checked with a profiler but couldn't find any significant bottleneck. I also complied it to cython (without making any changes) but that had no effect on speed either. Any suggestions are appreciated. The full listing is below:
__author__ = 'Nicolas Dickreuter'
import time
import numpy as np
from collections import Counter
class MonteCarlo(object):
def EvalBestHand(self, hands):
scores = [(i, self.score(hand)) for i, hand in enumerate(hands)]
winner = sorted(scores, key=lambda x: x[1], reverse=True)[0][0]
return hands[winner],scores[winner][1][-1]
def score(self, hand):
crdRanksOriginal = '23456789TJQKA'
originalSuits='CDHS'
rcounts = {crdRanksOriginal.find(r): ''.join(hand).count(r) for r, _ in hand}.items()
score, crdRanks = zip(*sorted((cnt, rank) for rank, cnt in rcounts)[::-1])
potentialThreeOfAKind = score[0] == 3
potentialTwoPair = score == (2, 2, 1, 1, 1)
potentialPair = score == (2, 1, 1, 1, 1, 1)
if score[0:2]==(3,2) or score[0:2]==(3,3): # fullhouse (three of a kind and pair, or two three of a kind)
crdRanks = (crdRanks[0],crdRanks[1])
score = (3,2)
elif score[0:4]==(2,2,2,1):
score=(2,2,1) # three pair are not worth more than two pair
sortedCrdRanks = sorted(crdRanks,reverse=True) # avoid for example 11,8,6,7
crdRanks=(sortedCrdRanks[0],sortedCrdRanks[1],sortedCrdRanks[2],sortedCrdRanks[3])
elif len(score) >= 5: # high card, flush, straight and straight flush
# straight
if 12 in crdRanks: # adjust if 5 high straight
crdRanks += (-1,)
sortedCrdRanks = sorted(crdRanks,reverse=True) # sort again as if pairs the first rank matches the pair
for i in range(len(sortedCrdRanks) - 4):
straight = sortedCrdRanks[i] - sortedCrdRanks[i + 4] == 4
if straight:
crdRanks=(sortedCrdRanks[i],sortedCrdRanks[i+1],sortedCrdRanks[i+2],sortedCrdRanks[i+3],sortedCrdRanks[i+4])
break
# flush
suits = [s for _, s in hand]
flush = max(suits.count(s) for s in suits) >= 5
if flush:
for flushSuit in originalSuits: # get the suit of the flush
if suits.count(flushSuit)>=5:
break
flushHand = [k for k in hand if flushSuit in k]
rcountsFlush = {crdRanksOriginal.find(r): ''.join(flushHand).count(r) for r, _ in flushHand}.items()
score, crdRanks = zip(*sorted((cnt, rank) for rank, cnt in rcountsFlush)[::-1])
crdRanks = tuple(sorted(crdRanks,reverse=True)) # ignore original sorting where pairs had influence
# check for straight in flush
if 12 in crdRanks: # adjust if 5 high straight
crdRanks += (-1,)
for i in range(len(crdRanks) - 4):
straight = crdRanks[i] - crdRanks[i + 4] == 4
# no pair, straight, flush, or straight flush
score = ([(5,), (2, 1, 2)], [(3, 1, 3), (5,)])[flush][straight]
if score == (1,) and potentialThreeOfAKind: score = (3, 1)
elif score == (1,) and potentialTwoPair: score = (2, 2, 1)
elif score == (1,) and potentialPair: score = (2, 1, 1)
if score[0]==5:
handType="StraightFlush"
#crdRanks=crdRanks[:5] # five card rule makes no difference {:5] would be incorrect
elif score[0]==4:
handType="FoufOfAKind"
crdRanks=crdRanks[:2]
elif score[0:2]==(3,2):
handType="FullHouse"
# crdRanks=crdRanks[:2] # already implmeneted above
elif score[0:3]==(3,1,3):
handType="Flush"
crdRanks=crdRanks[:5] # !! to be verified !!
elif score[0:3]==(3,1,2):
handType="Straight"
crdRanks=crdRanks[:5] # !! to be verified !!
elif score[0:2]==(3,1):
handType="ThreeOfAKind"
crdRanks=crdRanks[:3]
elif score[0:2]==(2,2):
handType="TwoPair"
crdRanks=crdRanks[:3]
elif score[0]==2:
handType="Pair"
crdRanks=crdRanks[:4]
elif score[0]==1:
handType="HighCard"
crdRanks=crdRanks[:5]
else: raise Exception('Card Type error!')
return score, crdRanks, handType
def createCardDeck(self):
values = "23456789TJQKA"
suites = "CDHS"
Deck=[]
[Deck.append(x+y) for x in values for y in suites]
return Deck
def distributeToPlayers(self, Deck, PlayerAmount, PlayerCardList, TableCardsList):
Players =[]
CardsOnTable = []
knownPlayers = 0
for PlayerCards in PlayerCardList:
FirstPlayer=[]
FirstPlayer.append(Deck.pop(Deck.index(PlayerCards[0])))
FirstPlayer.append(Deck.pop(Deck.index(PlayerCards[1])))
Players.append(FirstPlayer)
knownPlayers += 1
for c in TableCardsList:
CardsOnTable.append(Deck.pop(Deck.index(c))) # remove cards that are on the table from the deck
for n in range(0, PlayerAmount - knownPlayers):
plr=[]
plr.append(Deck.pop(np.random.random_integers(0,len(Deck)-1)))
plr.append(Deck.pop(np.random.random_integers(0,len(Deck)-1)))
Players.append(plr)
return Players, Deck
def distributeToTable(self, Deck, TableCardsList):
remaningRandoms = 5 - len(TableCardsList)
for n in range(0, remaningRandoms):
TableCardsList.append(Deck.pop(np.random.random_integers(0,len(Deck)-1)))
return TableCardsList
def RunMonteCarlo(self, originalPlayerCardList, originalTableCardsList, PlayerAmount, gui, maxRuns=6000,maxSecs=5):
winnerlist = []
EquityList = []
winnerCardTypeList=[]
wins = 0
runs=0
timeout_start=time.time()
for m in range(maxRuns):
runs+=1
try:
if gui.active==True:
gui.progress["value"] = int(round(m*100/maxRuns))
except:
pass
Deck = self.createCardDeck()
PlayerCardList = originalPlayerCardList[:]
TableCardsList = originalTableCardsList[:]
Players, Deck = self.distributeToPlayers(Deck, PlayerAmount, PlayerCardList, TableCardsList)
Deck5Cards = self.distributeToTable(Deck, TableCardsList)
PlayerFinalCardsWithTableCards = []
for o in range(0, PlayerAmount):
PlayerFinalCardsWithTableCards.append(Players[o]+Deck5Cards)
bestHand,winnerCardType=self.EvalBestHand(PlayerFinalCardsWithTableCards)
winner = (PlayerFinalCardsWithTableCards.index(bestHand))
#print (winnerCardType)
CollusionPlayers = 0
if winner < CollusionPlayers + 1:
wins += 1
winnerCardTypeList.append(winnerCardType)
# winnerlist.append(winner)
# self.equity=wins/m
# if self.equity>0.99: self.equity=0.99
# EquityList.append(self.equity)
if time.time()>timeout_start+maxSecs:
break
self.equity = wins / runs
self.winnerCardTypeList = Counter(winnerCardTypeList)
for key, value in self.winnerCardTypeList.items():
self.winnerCardTypeList[key] = value / runs
self.winTypesDict=self.winnerCardTypeList.items()
# show how the montecarlo converges
# xaxis=range(500,monteCarloRuns)
# plt.plot(xaxis,EquityList[499:monteCarloRuns])
# plt.show()
return self.equity,self.winTypesDict
if __name__ == '__main__':
Simulation = MonteCarlo()
mycards=[['AS', 'KS']]
cardsOnTable = []
players = 3
start_time = time.time()
Simulation.RunMonteCarlo(mycards, cardsOnTable, players, 1, maxRuns=200000, maxSecs=120)
print("--- %s seconds ---" % (time.time() - start_time))
equity = Simulation.equity # considering draws as wins
print (equity)
This algorithm is too long and complex for any sensible suggestion apart from generic ones. So here you go: vectorise everything you can and work with vectors of data instead of loops, lists, insertions, removals. Montecarlo simulations allow for this quite nicely because all samples are independent.
For example, you want 1 million retries, generate a numpy array of random 1 million values in one line. You want to test outcome, place 1 million outcomes into a numpy array and test all of them at once against a given condition generating a numpy array of booleans.
If you manage to vectorize this you will get tremendous performance increases.
Inefficient array operations are probably causing most of the slowness. In particular, I see a lot of lines like this:
Deck.pop(some_index)
Every time you do that, the list has to shift all the elements after that index. You can eliminate this (as well as repeated random functions) by using:
from random import shuffle
# Setup
originalDeck = createCardDeck()
# Individual run
Deck = originalDeck[:]
shuffle(Deck) # shuffles in place
# Draw a card from the end so nothing needs to shift
card = Deck.pop()
# Concise and efficient!
There are some other things you could do, like change long if-else chains into constant time lookups with a dict or custom class. Right now it checks for a straight flush first down to a high card last for every hand, which seems rather inefficient. I doubt that it would make a big difference though.