Egg dropping in worst case - function

I have been trying to write an algorithm to compute the maximum number or trials required in worst case, in the egg dropping problem. Here is my python code
def eggDrop(n,k):
eggFloor=[ [0 for i in range(k+1) ] ]* (n+1)
for i in range(1, n+1):
eggFloor[i][1] = 1
eggFloor[i][0] = 0
for j in range(1, k+1):
eggFloor[1][j] = j
for i in range (2, n+1):
for j in range (2, k+1):
eggFloor[i][j] = 'infinity'
for x in range (1, j + 1):
res = 1 + max(eggFloor[i-1][x-1], eggFloor[i][j-x])
if res < eggFloor[i][j]:
eggFloor[i][j] = res
return eggFloor[n][k]print eggDrop(2, 100)
```
The code is outputting a value of 7 for 2eggs and 100floors, but the answer should be 14, i don't know what mistake i have made in the code. What is the problem?

The problem is in this line:
eggFloor=[ [0 for i in range(k+1) ] ]* (n+1)
You want this to create a list containing (n+1) lists of (k+1) zeroes. What the * (n+1) does is slightly different - it creates a list containing (n+1) copies of the same list.
This is an important distinction - because when you start modifying entries in the list - say,
eggFloor[i][1] = 1
this actually changes element [1] of all of the lists, not just the ith one.
To instead create separate lists that can be modified independently, you want something like:
eggFloor=[ [0 for i in range(k+1) ] for j in range(n+1) ]
With this modification, the program returns 14 as expected.
(To debug this, it might have been a good idea to write out a function to pring out the eggFloor array, and display it at various points in your program, so you can compare it with what you were expecting. It would soon become pretty clear what was going on!)

Related

Csv reader PyQt5 [duplicate]

I've an iterable list of over 100 elements. I want to do something after every 10th iterable element. I don't want to use a counter variable. I'm looking for some solution which does not includes a counter variable.
Currently I do like this:
count = 0
for i in range(0,len(mylist)):
if count == 10:
count = 0
#do something
print i
count += 1
Is there some way in which I can omit counter variable?
for count, element in enumerate(mylist, 1): # Start counting from 1
if count % 10 == 0:
# do something
Use enumerate. Its built for this
Just to show another option...hopefully I understood your question correctly...slicing will give you exactly the elements of the list that you want without having to to loop through every element or keep any enumerations or counters. See Explain Python's slice notation.
If you want to start on the 1st element and get every 10th element from that point:
# 1st element, 11th element, 21st element, etc. (index 0, index 10, index 20, etc.)
for e in myList[::10]:
<do something>
If you want to start on the 10th element and get every 10th element from that point:
# 10th element, 20th element, 30th element, etc. (index 9, index 19, index 29, etc.)
for e in myList[9::10]:
<do something>
Example of the 2nd option (Python 2):
myList = range(1, 101) # list(range(1, 101)) for Python 3 if you need a list
for e in myList[9::10]:
print e # print(e) for Python 3
Prints:
10
20
30
...etc...
100
for i in range(0,len(mylist)):
if (i+1)%10==0:
do something
print i
A different way to approach the problem is to split the iterable into your chunks before you start processing them.
The grouper recipe does exactly this:
from itertools import izip_longest # needed for grouper
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
You would use it like this:
>>> i = [1,2,3,4,5,6,7,8]
>>> by_twos = list(grouper(i, 2))
>>> by_twos
[(1, 2), (3, 4), (5, 6), (7, 8)]
Now, simply loop over the by_twos list.
You can use range loops to iterate through the length of mylist in multiples of 10 the following way:
for i in range(0,len(mylist), 10):
#do something

Pyevolve - sum of chromosome = 1

# Genome instance, 1D List of 20 elements
genome = G1DList.G1DList(20)
Sets the range max and min of the 1D List
genome.setParams(rangemin=0, rangemax=1)
Change the initializator to Real values
genome.initializator.set(Initializators.G1DListInitializatorReal)
This give 20 elements between 0 and 1. I need that the sum of all elements in the chromosome to be equal to 1 . Any idea how to do this?
I found the answer. I created my own Initializator which is a modified copy of G1DListInitializatorReal:
def G1DListInitializatorRealSumEqualOne(genome, **args):
""" Real initialization function of G1DList
This initializator accepts the *rangemin* and *rangemax* genome parameters.
"""
range_min = genome.getParam("rangemin", 0)
range_max = genome.getParam("rangemax", 100)
genome.genomeList = [random.uniform(range_min, range_max) for i in xrange(genome.getListSize())]
genome.genomeList[:] = [x / sum(genome.genomeList) for x in genome.genomeList]
And then changed the code in my question to:
# Genome instance, 1D List of 20 elements
genome = G1DList.G1DList(20)
Sets the range max and min of the 1D List
genome.setParams(rangemin=0, rangemax=1)
Change the initializator to Real values
genome.initializator.set(G1DListInitializatorRealSumEqualOne)

csv empty strings handling and values appending

With a csv of ~50 rows (stars) and ~30 columns (name, magnitudes and distance), that has some empty string values (''), I am trying to do two things in which all the help so far hasn't been useful. (1) I need to parse empty strings as 0.0, so I can (2) append each row in a list of lists (what I called s).
In other words:
- s is a list of stars (each one has all its parameters)
- d is a particular parameter for all the stars (distance), which I obtain correctly.
Big issue is with s. My try:
with open('stars.csv', 'r') as mycsv:
csv_stars = csv.reader(mycsv)
next(csv_stars) #skip header
stars = list(csv_stars)
s = [] # star
d = [] # distances
for row in stars:
row[row==''] = '0'
s.append(float(row)) #stars
d.append(arcsec*AU*float(row[30]))
I can't think of a better syntax, and so I get the error
s.append(float(row)) # stars
TypeError: float() argument must be a string or a number
From s I would obtain later the magnitudes for all the stars, separately. But first things first...
#cwasdwa Please look at below code. it will give you an idea. I am sure there might be better way. This solution is based on what I have understood from your code.
with open('stars.csv', 'r') as mycsv:
csv_stars = csv.reader(mycsv)
next(csv_stars) #skip header
stars = list(csv_stars)
s = [] # star
d = [] # distances
for row in stars:
newRow = [] #create new row array to convert all '' to 0.0
for x in row:
if x =='':
newRow.append(0.0)
else:
newRow.append(x)
s.append(newRow) #stars
if row[30] == '':
value = 0.0
else:
value = row[30]
d.append(arcsec*AU*float(value))

for every row, reshape and calculate eigenvectors in a vectorized way

I have been wanting to figure this out for a long time, but have had no success yet. I am assuming I will use arrayfun, but I couldn't figure it yet. Appreciate help. Here is the problem:
Given a matrix of many rows and N^2 columns, reshape every row to NxN matrix and calculate eigenvalues, and do this in a vectorized way not using for loop. For example
A=
0.6060168 0.8340029 0.0064574 0.7133187
0.6325375 0.0919912 0.5692567 0.7432627
0.8292699 0.5136958 0.4171895 0.2530783
0.7966113 0.1975865 0.6687064 0.3226548
0.0163615 0.2123476 0.9868179 0.1478827
for every **i**
m=reshape(A(i,:),2,2)
[vc vl]=eig(m)
I am inclined to do something like
f = #(x) eig(reshape(x,2,2))
arrayfun(f,A)
but of course I am getting an error like
octave:5> arrayfun(f,A)
error: reshape: can't reshape 1x1 array to 2x2 array
error: evaluating argument list element number 1
error: evaluating argument list element number 1
error: called from:
error: at line -1, column -1
error: cellfun: too many output arguments
error: /usr/share/octave/3.2.4/m/general/arrayfun.m at line 168, column 21
A = [0.6060168 0.8340029 0.0064574 0.7133187;
0.6325375 0.0919912 0.5692567 0.7432627;
0.8292699 0.5136958 0.4171895 0.2530783;
0.7966113 0.1975865 0.6687064 0.3226548;
0.0163615 0.2123476 0.9868179 0.1478827];
N = 2;
[mc, ml] = arrayfun (#(row) eig (reshape (A (row, :), N, N)), 1:rows(A), "UniformOutput", false)
mc =
{
[1,1] =
-0.170783 -0.044626
0.985309 -0.999004
[1,2] =
-0.95343 -0.89053
0.30161 -0.45492
(cropped)
}
ml =
{
[1,1] =
Diagonal Matrix
0.56876 0
0 0.75057
[1,2] =
Diagonal Matrix
0.45246 0
0 0.92334
(cropped)
With the Ndpar package, calculations can be parallelized over multiple cores.
Borrowing from Andy's answer,
pkg load ndpar
A = [0.6060168 0.8340029 0.0064574 0.7133187;
0.6325375 0.0919912 0.5692567 0.7432627;
0.8292699 0.5136958 0.4171895 0.2530783;
0.7966113 0.1975865 0.6687064 0.3226548;
0.0163615 0.2123476 0.9868179 0.1478827];
N = 2;
[eigenvectors, eigenvalues] = ndpar_arrayfun(nproc,
#(row) eig(reshape(row, N, N)),
A, "IdxDimensions", 1, "Uniformoutput", false)
yields the same output.
EDIT - Or with the original pararrayfun from the octave-forge parallel package:
[eigenvectors, eigenvalues] = pararrayfun(nproc,
#(row_idx) eig(reshape(A(row_idx, :), N, N)),
1:rows(A), "UniformOutput", false)

What's the correct way to expand a [0,1] interval to [a,b]?

Many random-number generators return floating numbers between 0 and 1.
What's the best and correct way to get integers between a and b?
Divide the interval [0,1] in B-A+1 bins
Example A=2, B=5
[----+----+----+----]
0 1/4 1/2 3/4 1
Maps to 2 3 4 5
The problem with the formula
Int (Rnd() * (B-A+1)) + A
is that your Rnd() generation interval is closed on both sides, thus the 0 and the 1 are both possible outputs and the formula gives 6 when the Rnd() is exactly 1.
In a real random distribution (not pseudo), the 1 has probability zero. I think it is safe enough to program something like:
r=Rnd()
if r equal 1
MyInt = B
else
MyInt = Int(r * (B-A+1)) + A
endif
Edit
Just a quick test in Mathematica:
Define our function:
f[a_, b_] := If[(r = RandomReal[]) == 1, b, IntegerPart[r (b - a + 1)] + a]
Build a table with 3 10^5 numbers in [1,100]:
table = SortBy[Tally[Table[f[1, 100], {300000}]], First]
Check minimum and maximum:
In[137]:= {Max[First /# table], Min[First /# table]}
Out[137]= {100, 1}
Lets see the distribution:
BarChart[Last /# SortBy[Tally[Table[f[1, 100], {300000}]], First],
ChartStyle -> "DarkRainbow"]
X = (Rand() * (B - A)) + A
Another way to look at it, where r is your random number in the range 0 to 1:
(1-r)a + rb
As for your additional requirement of the result being an integer, maybe (apart from using built in casting) the modulus operator can help you out. Check out this question and the answer:
Expand a random range from 1–5 to 1–7
Well, why not just look at how Python does it itself? Read random.py in your installation's lib directory.
After gutting it to only support the behavior of random.randint() (which is what you want) and removing all error checks for non-integer or out-of-bounds arguments, you get:
import random
def randint(start, stop):
width = stop+1 - start
return start + int(random.random()*width)
Testing:
>>> l = []
>>> for i in range(2000000):
... l.append(randint(3,6))
...
>>> l.count(3)
499593
>>> l.count(4)
499359
>>> l.count(5)
501432
>>> l.count(6)
499616
>>>
Assuming r_a_b is the desired random number between a and b and r_0_1 is a random number between 0 and 1 the following should work just fine:
r_a_b = (r_0_1 * (b-a)) + a