Lua Function stuck in a loop - function

I'm using the standard lua 5.4.4 distribution and interpreter, no libraries, etc.
I'm attempting to write a script to evaluate user defined values of the Collatz Conjecture
(TLDR at the bottom)
I want the script to read a user input (this part works so I didn't include it)
and based on that input, set a starting number (ognumber), and a range of numbers to try
then, i want it to iterate through every number between the ognumber and the range (in increments of 1)
for every iteration, i want it to check if each number is odd or even, do some math with the result being "newnumber", then set number = newnumber to try the result of the previous operation.
The problem I'm facing is that when I call the function, it doesn't end.
it gets stuck continuously trying the same number, it doesn't return number, and it doesn't set number = newnumber.
function isodd(number, newnumber, ResultsPath, results)
newnumber = math.floor(number * 3 + 1)
print(number..' x 3 + 1 = '..newnumber)
local writetoresultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
writetoresultsfile:write(' '..number..' x 3 + 1 = '..newnumber..'\n')
writetoresultsfile:close()
number = math.floor(newnumber)
return number
end
function iseven(number, newnumber, ResultsPath, results)
newnumber = math.floor(number / 2)
print(number..' / 2 = '..newnumber)
local writetoresultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
writetoresultsfile:write(' '..number..' / 2 = '..newnumber..'\n')
writetoresultsfile:close()
number = math.floor(newnumber)
return number
end
for number = ognumber, range, 1 do
local writetoresultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
writetoresultsfile:write(' <List name = "Attempt '..ognumber..'"> \n')
writetoresultsfile:close()
print('Trying '..number)
while(number > 1) do
if(number % 2 ~= 0) then
isodd(number, newnumber, ResultsPath, results)
else
iseven(number, newnumber, ResultsPath, results)
end
end
ognumber = ognumber + 1
number = ognumber
end
if(ognumber == range + 1) then
local resultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
resultsfile:write(' </Table>\n')
resultsfile:write('</Data>\n')
resultsfile:close()
end
This is what shows up when I run the script using function()
This script works, but I wanted to achieve the same thing using functions
for number = ognumber, range, 1 do
local writetoresultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
writetoresultsfile:write(' <List name = "Attempt '..ognumber..'"> \n')
writetoresultsfile:close()
print('Trying '..number)
while(number > 1) do
if(number % 2 ~= 0) then
newnumber = math.floor(number * 3 + 1)
print(number..' x 3 + 1 = '..newnumber)
local writetoresultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
writetoresultsfile:write(' '..number..' x 3 + 1 = '..newnumber..'\n')
writetoresultsfile:close()
number = newnumber
else
newnumber = math.floor(number / 2)
print(number..' / 2 = '..newnumber)
local writetoresultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
writetoresultsfile:write(' '..number..' / 2 = '..newnumber..'\n')
writetoresultsfile:close()
number = newnumber
end
end
ognumber = ognumber + 1
number = ognumber
end
if(ognumber == range + 1) then
local resultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
resultsfile:write(' </Table>\n')
resultsfile:write('</Data>\n')
resultsfile:close()
end
This is what it shows when I don't use function()
Edit: This Script works using the advice Shingo gave me
function isodd(number, ResultsPath, results)
newnumber = math.floor(number * 3 + 1)
print(number..' x 3 + 1 = '..newnumber)
local writetoresultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
writetoresultsfile:write(' '..number..' x 3 + 1 = '..newnumber..'\n')
writetoresultsfile:close()
number = math.floor(newnumber)
return number
end
function iseven(number, ResultsPath, results)
newnumber = math.floor(number / 2)
print(number..' / 2 = '..newnumber)
local writetoresultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
writetoresultsfile:write(' '..number..' / 2 = '..newnumber..'\n')
writetoresultsfile:close()
number = math.floor(newnumber)
return number
end
for number = ognumber, range, 1 do
local writetoresultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
writetoresultsfile:write(' <List name = "Attempt '..ognumber..'"> \n')
writetoresultsfile:close()
print('Trying '..number)
while(number > 1) do
if(number % 2 ~= 0) then
number = isodd(number, ResultsPath, results)
else
number = iseven(number, ResultsPath, results)
end
end
ognumber = ognumber + 1
number = ognumber
end
if(ognumber == range + 1) then
local resultsfile = io.open(ResultsPath..'3n+1-'..results..'.xml', 'a')
resultsfile:write(' </Table>\n')
resultsfile:write('</Data>\n')
resultsfile:close()
end
TLDR about the Collatz Conjecture for those interested:
The Collatz Conjecture states that for any positive integer, if you apply 2 rules to it, will always return to 1.
The rules are: if the previous number is even, the next number is one half of the previous number. If the previous number is odd, the next number is 3 times the previous number plus 1

Although you return number from the function, it won't be updated out of the scope, you need accept it like this (same as iseven):
number = isodd(number, newnumber, ResultsPath, results)
Moreover newnumber is only used inside the function, so it's not necessary to be an argument. The function could be change to:
function isodd(number, ResultsPath, results)
local newnumber = math.floor(number * 3 + 1)

Related

How to calculate a probability vector and an observation count vector for a range of bins?

I want to test the hypothesis whether some 30 occurrences should fit a Poisson distribution.
#GNU Octave
X = [8 0 0 1 3 4 0 2 12 5 1 8 0 2 0 1 9 3 4 5 3 3 4 7 4 0 1 2 1 2]; #30 observations
bins = {0, 1, [2:3], [4:5], [6:20]}; #each bin can be single value or multiple values
I am trying to use Pearson's chi-square statistics here and coded the below function. I want a Poisson vector to contain corresponding Poisson probabilities for each bin and count the observations for each bin. I feel the loop is rather redundant and ugly. Can you please let me know how can I re-factor the function without the loop and make the whole calculation cleaner and more vectorized?
function result= poissonGoodnessOfFit(bins, observed)
assert(iscell(bins), "bins should be a cell array");
assert(all(cellfun("ismatrix", bins)) == 1, "bin entries either scalars or matrices");
assert(ismatrix(observed) && rows(observed) == 1, "observed data should be a 1xn matrix");
lambda_head = mean(observed); #poisson lambda parameter estimate
k = length(bins); #number of bin groups
n = length(observed); #number of observations
poisson_probability = []; #variable for poisson probability for each bin
observations = []; #variable for observation counts for each bin
for i=1:k
if isscalar(bins{1,i}) #this bin contains a single value
poisson_probability(1,i) = poisspdf(bins{1, i}, lambda_head);
observations(1, i) = histc(observed, bins{1, i});
else #this bin contains a range of values
inner_bins = bins{1, i}; #retrieve the range
inner_bins_k = length(inner_bins); #number of values inside
inner_poisson_probability = []; #variable to store individual probability of each value inside this bin
inner_observations = []; #variable to store observation counts of each value inside this bin
for j=1:inner_bins_k
inner_poisson_probability(1,j) = poisspdf(inner_bins(1, j), lambda_head);
inner_observations(1, j) = histc(observed, inner_bins(1, j));
endfor
poisson_probability(1, i) = sum(inner_poisson_probability, 2); #assign over the sum of all inner probabilities
observations(1, i) = sum(inner_observations, 2); #assign over the sum of all inner observation counts
endif
endfor
expected = n .* poisson_probability; #expected observations if indeed poisson using lambda_head
chisq = sum((observations - expected).^2 ./ expected, 2); #Pearson Chi-Square statistics
pvalue = 1 - chi2cdf(chisq, k-1-1);
result = struct("actual", observations, "expected", expected, "chi2", chisq, "pvalue", pvalue);
return;
endfunction
There's a couple of things worth noting in the code.
First, the 'scalar' case in your if block is actually identical to your 'range' case, since a scalar is simply a range of 1 element. So no special treatment is needed for it.
Second, you don't need to create such explicit subranges, your bin groups seem to be amenable to being used as indices into a larger result (as long as you add 1 to convert from 0-indexed to 1-indexed indices).
Therefore my approach would be to calculate the expected and observed numbers over the entire domain of interest (as inferred from your bin groups), and then use the bin groups themselves as 1-indices to obtain the desired subgroups, summing accordingly.
Here's an example code, written in the octave/matlab compatible subset of both languges:
function Result = poissonGoodnessOfFit( BinGroups, Observations )
% POISSONGOODNESSOFFIT( BinGroups, Observations) calculates the [... etc, etc.]
pkg load statistics; % only needed in octave; for matlab buy statistics toolbox.
assert( iscell( BinGroups ), 'Bins should be a cell array' );
assert( all( cellfun( #ismatrix, BinGroups ) ) == 1, 'Bin entries either scalars or matrices' );
assert( ismatrix( Observations ) && rows( Observations ) == 1, 'Observed data should be a 1xn matrix' );
% Define helpful variables
RangeMin = min( cellfun( #min, BinGroups ) );
RangeMax = max( cellfun( #max, BinGroups ) );
Domain = RangeMin : RangeMax;
LambdaEstimate = mean( Observations );
NBinGroups = length( BinGroups );
NObservations = length( Observations );
% Get expected and observed numbers per 'bin' (i.e. discrete value) over the *entire* domain.
Expected_Domain = NObservations * poisspdf( Domain, LambdaEstimate );
Observed_Domain = histc( Observations, Domain );
% Apply BinGroup values as indices
Expected_byBinGroup = cellfun( #(c) sum( Expected_Domain(c+1) ), BinGroups );
Observed_byBinGroup = cellfun( #(c) sum( Observed_Domain(c+1) ), BinGroups );
% Perform a Chi-Square test on the Bin-wise Expected and Observed outputs
O = Observed_byBinGroup; E = Expected_byBinGroup ; df = NBinGroups - 1 - 1;
ChiSquareTestStatistic = sum( (O - E) .^ 2 ./ E );
PValue = 1 - chi2cdf( ChiSquareTestStatistic, df );
Result = struct( 'actual', O, 'expected', E, 'chi2', ChiSquareTestStatistic, 'pvalue', PValue );
end
Running with your example gives:
X = [8 0 0 1 3 4 0 2 12 5 1 8 0 2 0 1 9 3 4 5 3 3 4 7 4 0 1 2 1 2]; % 30 observations
bins = {0, 1, [2:3], [4:5], [6:20]}; % each bin can be single value or multiple values
Result = poissonGoodnessOfFit( bins, X )
% Result =
% scalar structure containing the fields:
% actual = 6 5 8 6 5
% expected = 1.2643 4.0037 13.0304 8.6522 3.0493
% chi2 = 21.989
% pvalue = 0.000065574
A general comment about the code; it is always preferable to write self-explainable code, rather than code that does not make sense by itself in the absence of a comment. Comments generally should only be used to explain the 'why', rather than the 'how'.

Why does the fibonacci recursive sequence work?

I am wondering why this Fibonacci recursive function works:
int fibRec(int n)
{
if ((n == 1) || (n == 0))
{
return n;
}
int i = fibRec(n - 1) + fibRec(n - 2);
return i;
}
I understand what the Fibonacci sequence is and I understand what a recursive function does and how this function is working. I'm just having troubles understanding why it works. I know that when you break it down, you are essentially adding a bunch of 0s and 1s, as this image depicts.
fibonacci recursive
But why is it that when I pass a 5 to the function and all the 0 and 1s are added that it will equal the 5th sequence number in the Fibonacci sequence? I've seen this question asked before but never really explained. The responses are all just "because recursion". Yes, I know what a recursive function is and how this one is working. But WHY does this recursive function give you the correct Fibonacci sequence number?
In the Fibonacci sequence the first two numbers are zero and one. Every number after these is the sum of the previous 2 numbers. So the first few numbers are
F(0) ≡ 0
F(1) ≡ 1
F(2) = F(1) + F(0) = 1 + 0 = 1
F(3) = F(2) + F(1) = 1 + 1 = 2
F(4) = F(3) + F(2) = 2 + 1 = 3
F(5) = F(4) + F(3) = 3 + 2 = 5
F(6) = F(5) + F(4) = 5 + 3 = 8
...
F(n) = F(n - 1) + F(n - 2) ∀ n > 1
Therefore when we calculate a Fibonacci number recursively we have to practice the following logical procedure (in pseudo-code out of respect to StackOverflow).
Integer NthFibonacci(Integer n) {
if (n < 0) {
return undefined;
} else if (n < 2) {
return n;
} else {
return NthFibonacci(n - 1) + NthFibonacci(n - 2);
}
}
I'm sure you know all this but I think it will help my explanation to have this part as a reference.
Where the Ones and Zeros Come In
The best way to explain this is probably with an example.
Imagine that, as above, we are trying to recursively calculate F(6). Try following the procedure given above. Remember that we will perform recursion only if n > 1.
First we start with F(6) = F(5) + F(4).
Then we find F(5) = F(4) + F(3).
Then we find F(4) = F(3) + F(2).
Then we find F(3) = F(2) + F(1).
Then we find F(2) = F(1) + F(0).
This is where things start to work out!
We have now gotten F(2) in terms of F(1) ≡ 1 and F(0) ≡ 0 (both of which are known), and so we are able to calculate an actual value instead of performing more recursion.
We can now find F(2) = F(1) + F(0) = 1 + 0 = 1.
NOTICE THE 1 AND 0 Those are what people are talking about when they say the whole thing comes down to ones and zeros. Every time we recurse down to find a base value we will end up finding F(2) = 1 + 0. This leads to more ones and zeros as we move back up our recursion tree being able to calculate higher and higher values, as follows.
F(3) = F(2) + F(1) = (1 + 0) + 1
F(4) = F(3) + F(2) = ((1 + 0) + 1) + (1 + 0)
F(5) = F(4) + F(3) = (((1 + 0) + 1) + (1 + 0)) + ((1 + 0) + 1)
F(6) = F(5) + F(4) = ((((1 + 0) + 1) + (1 + 0)) + ((1 + 0) + 1)) + (((1 + 0) + 1) + (1 + 0))
Now if you add up all the 1's you get a sum of 8, and so F(6) = 8, which is correct!
This is how it works, and this is how it breaks down to ones and zeros.
Remember, recursion works by breaking down the problem till we know what the answer is, and then building it up from there.
What do we know about the fibonacci sequence?
We know that when:
x = 1
and
x = 0
That that is the lowest it goes. That is an important key. Because when x = 0 we are really doing 0 + 0 and when x = 1 we are really doing 0 + 1. Now start at the top.
0,1,1,2,3,5,8,13...
If we are at 13. what is 13? Why simply 5 + 8 right? So That is where
int i = fibRec(n - 1) + fibRec(n - 2);
comes from. Because these are going to branch out lower and lower till we are at a base case for each one.
This is the recursive calling. Because now the method is going to go back to the stack and call fibRec again. You will notice that (n-1) and (n-2) are both added together and set to i. This is so that we don't lose the value. because of the + sign the stack then ends up returning more and more (n-1)s and (n-2)s until we are at the base case. I hope all of this makes sense. Thinking recursively can be very difficult. Here is a a visual representation from top to bottom of what it would look like.
In short. This just keeps adding the previous fibonacci sequences to the current one until it gets to the current loop.

Pandas function that iterates over values in a series with case statements

I have a dataframe that has contains a column of integers. I want to write a function that takes a series as an argument, iterates through each value of the series, and performs a case statement on each integer within the series, and returns a new series from the results of the case statement. Currently I'm working with the following code and getting errors:
def function(series):
if series['column_of_ints'] >= 0 and series['column_of_ints'] < 100:
return series['column_of_ints']
elif series['column_of_ints'] >= 100 and series['column_of_ints'] < 200:
return series['column_of_ints'] + 1
else:
return series['column_of_ints'] + 2
df['column_of_ints_v2'] = df['column_of_ints'].apply(function, axis=1)
Don't use apply you can achieve the same result much faster using 3 .loc calls:
df.loc[(df['column_of_ints'] >= 0) & (df['column_of_ints'] < 100), 'column_of_ints_v2'] df['column_of_ints']
df.loc[(df['column_of_ints'] >= 100) & (df['column_of_ints'] < 200), 'column_of_ints_v2'] = df['column_of_ints'] + 1
df.loc[(df['column_of_ints'] < 0) & (df['column_of_ints'] >= 200), 'column_of_ints_v2'] = df['column_of_ints'] + 2
Or using where:
df['column_of_ints_v2'] = np.where((df['column_of_ints'] >= 0) & (df['column_of_ints') < 100), df['column_of_ints'] + 1, np.where( (df['column_of_ints'] >= 100) & (df['column_of_ints'] < 200), df['column_of_ints'] + 2, df['column_of_ints'] ))
As to why your code fails:
df['column_of_ints'].apply(function, axis=1)
df['column_of_ints'] is a Series not a DataFrame, there is no axis=1 for apply method for a Series, you can force this to a DataFrame using double square brackets:
df[['column_of_ints']].apply(function, axis=1)
If you're applying row-wise to a single column then you don't need the column accessors in your function:
def function(series):
if series >= 0 and series < 100:
return series
elif series >= 100 and series < 200:
return series + 1
else:
return series + 2
but really you should use a vectorised method like my proposal above

improving a function in python

I created this function to figure out if betting either side of proposition bet on how many 3 pointers there will be in a particular basketball game is profitable. I project how many total three pointers will be made pjTotal3s and the standard deviation pjGame3STD earlier in the code. The threes_over is the the number given to me by the betting site for which I try to find if the total number of threes will be over or under that number. In this case it is 14.5.
threes_over = 14.5
def overunder(n):
over_count = 0
under_count = 0
push_count = 0
overodds = 0
underodds = 0
for i in range(n):
if round(np.random.normal(pjTotal3s,pjGame3STD)) > threes_over:
over_count = over_count + 1
if round(np.random.normal(pjTotal3s,pjGame3STD)) < threes_over:
under_count = under_count +1
if round(np.random.normal(pjTotal3s,pjGame3STD)) == threes_over:
push_count = push_count + 1
return over_count, under_count, push_count
Then I simulate it a 100,000 overunder(100000) times and it gives me how many times the number of three pointers will be over, under or equal to the number given. This works fine but I still have to more work to do to find if it is a profitable bet.
Assuming that this the output (57550, 42646, 0) I have to manually input it like so and do more to find out if either side of the bet is worthwhile.
over_count = 57550
under_count = 42646
over = 1/(over_count / (over_count + under_count))
under = 1/ (under_count / (over_count + under_count))
over_odds_given = 1.77
under_odds_given = 2.05
overedge = 1/over * over_odds_given - 1
underedge = 1/under * under_odds_given - 1
print overedge, underedge
How do I combine the second set of calculations into the same function as the first. I would like to avoid having to manually input the results of the first function in order to save time and avoid inputting a wrong number.
If you really want the second bit of code in the same function as the first, you could just paste all but the part where you set the over_count and under_count into the function...
def overunder(n):
over_count = 0
under_count = 0
push_count = 0
overodds = 0
underodds = 0
for i in range(n):
if round(np.random.normal(pjTotal3s,pjGame3STD)) > threes_over:
over_count = over_count + 1
if round(np.random.normal(pjTotal3s,pjGame3STD)) < threes_over:
under_count = under_count +1
if round(np.random.normal(pjTotal3s,pjGame3STD)) == threes_over:
push_count = push_count + 1
over = 1/(over_count / float(over_count + under_count))
under = 1/ (under_count / float(over_count + under_count))
over_odds_given = 1.77
under_odds_given = 2.05
overedge = 1/over * over_odds_given - 1
underedge = 1/under * under_odds_given - 1
print overedge, underedge
return over_count, under_count, push_count
Or, probably better, you could put the second bit of code (overedge, underedge) into a separate function and pass it the results from overunder:
def edges(over_count, under_count, push_count):
over = 1/(over_count / float(over_count + under_count))
under = 1/ (under_count / float(over_count + under_count))
over_odds_given = 1.77
under_odds_given = 2.05
overedge = 1/over * over_odds_given - 1
underedge = 1/under * under_odds_given - 1
print overedge, underedge
And then call it with the results from overunder:
c = overunder(100000)
edges(c[0],c[1],c[2])

I'm trying to do Ackermann's Function on IBasic

Ackermann's Function is a recursive mathematical algorithm that can be used to test how well a computer performs recursion. Design a function ackermann(m,n), which solves Ackermann's Function. Use the following logic in your function:
If m = 0, then return n + 1
If n = 0 then return ackermann(m-1, 1)
Otherwise, return ackermann(m-1, ackermann(m, n-1))
The program stops after it hits 13. Can anyone tell me what I have done wrong?
declare main()
declare ackermann(m:int, n:int)
openconsole
main()
print:print "Press any key to quit...",
do:until inkey$<>""
closeconsole
end
sub main()
def counter, m, n:int
counter = 0
for counter = 1 to 100
print ackermann(counter)
next counter
return
sub ackermann(m, n)
if m = 0
return = n + 1
else
if n = 0
return = ackermann(m - 1, 1)
else
return = ackermann(m - 1, ackermann(m, n - 1))
endif
endif
return
Your print statement is
print ackermann(counter)
but your function is
sub ackermann(m, n)
You need to send a second parameter in the first call.
Do note that Ackermann's function grows incredibly quickly - values above m,n > (3,4) are going to have a LOT of digits and if you go more than even about (4,4) you'll quickly find numbers that could quite possibly fill up your memory entirely with digits...
Refer to Wikipedia to see the magnitude of the numbers you are trying to compute...
declare main()
declare ackermann(m:int, n:int)
openconsole
main()
print:print "Press any key to quit...",
do:until inkey$<>""
closeconsole
end
sub main()
print Ackermann(3,5)
return
sub ackermann(m, n)
if m = 0
return = n + 1
else
if n = 0
return = ackermann(m - 1, 1)
else
return = ackermann(m - 1, ackermann(m, n - 1))
endif
endif
return