I am looking to implement a linear congruential generator in Excel.
As we know, we must choose the parameter of LCG is a, c, m, and Z0.
Wikipedia says that
The period of a general LCG is at most m, and for some choices of
factor a much less than that. The LCG will have a full period for all
seed values if and only if:
m and the offset c are relatively prime,
a - 1 is divisible by all prime factors of m,
a - 1 is divisible by 4 if m is divisible by 4.
Also,
m, 0 < m – the "modulus"
a, 0 < a < m – the "multiplier"
c, 0 < c < m – the "increment"
Z0, 0 < Z0 < m – the "seed" or "start value"
I need to choose those values, I want Z0 initial value is 10113383, and the rest is random. Nah, what values that has a specified period and guaranteed no collisions for the duration of that period?
I've tried to put some values, a=13, c=911, m=11584577 and it looks no collisions. But I'm not sure if I break the rules or not.
I regularly teach a number theory and cryptography course so have built up a library of programs in VBA and Python. Using these, I only needed to write one more:
Function GCD(num1 As Long, num2 As Long) As Long
Dim a As Long
Dim b As Long
a = num1
b = num2
Dim R As Long
R = 1
Do Until R = 0
R = a Mod b
a = b
b = R
Loop
GCD = a
End Function
Sub Helper_Factor(ByVal n As Long, ByVal p As Long, factors As Collection)
'Takes a passed collection and adds to it an array of the form
'(q,k) where q >= p is the smallest prime divisor of n
'p is assumed to be odd
'The function is called in such a way that
'the first divisor found is automatically prime
Dim q As Long, k As Long
q = p
Do While q <= Sqr(n)
If n Mod q = 0 Then
k = 1
Do While n Mod q ^ k = 0
k = k + 1
Loop
k = k - 1 'went 1 step too far
factors.Add Array(q, k)
Helper_Factor n / q ^ k, q + 2, factors
Exit Sub
End If
q = q + 2
Loop
'if we get here then n is prime - add it as a factor
factors.Add Array(n, 1)
End Sub
Function Factor(ByVal n As Long) As Collection
Dim factors As New Collection
Dim k As Long
Do While n Mod 2 ^ k = 0
k = k + 1
Loop
k = k - 1
If k > 0 Then
n = n / 2 ^ k
factors.Add Array(2, k)
End If
If n > 1 Then Helper_Factor n, 3, factors
Set Factor = factors
End Function
Function DisplayFactors(n As Long) As String
Dim factors As Collection
Dim i As Long, p As Long, k As Long
Dim sfactors As Variant
Set factors = Factor(n)
ReDim sfactors(1 To factors.Count)
For i = 1 To factors.Count
p = factors(i)(0)
k = factors(i)(1)
sfactors(i) = p & IIf(k > 1, "^" & k, "")
Next i
DisplayFactors = Join(sfactors, "*")
End Function
Function MaxPeriod(a As Long, c As Long, m As Long) As Boolean
'assumes 0 < a,c < m
Dim factors As Collection
Dim p As Long, i As Long
If GCD(c, m) > 1 Then Exit Function 'with default value of False
If m Mod 4 = 0 And (a - 1) Mod 4 > 0 Then Exit Function
'else:
Set factors = Factor(m)
For i = 1 To factors.Count
p = factors(i)(0)
If p < m And (a - 1) Mod p > 0 Then Exit Function
Next i
'if you survive to here:
MaxPeriod = True
End Function
For example, in the Intermediate Window you can check:
?maxperiod(13,911,11584577)
True
so you seem to be in luck
Related
I have a list of words and I would like to find all symmetric words and some how put value 1 for each of them (see the picture).
This does the trick:
=1*(A1=CONCAT(MID(A1,LEN(A1)-SEQUENCE(1,LEN(A1),0),1)))
It reads the string in a cell backwards using MID and SEQUENCE, and compares the CONCAT result with the original to see if it is the same, i.e. the string is symmetric.
Multiplying by 1 forces the Boolean into an integer.
With VBA. This assumes that a single character is symmetric:
Public Function Sym(s As String) As Long
Dim L As Long, L2 As Long
Dim p1 As String, p2 As String
L = Len(s)
L2 = Int(L / 2)
Sym = 0
If L Mod 2 = 0 Then
' even
p1 = Mid(s, 1, L2)
p2 = StrReverse(Mid(s, L2 + 1))
If p1 = p2 Then
Sym = 1
End If
Else
' odd
p1 = Mid(s, 1, L2)
p2 = StrReverse(Mid(s, L2 + 2))
If p1 = p2 Then
Sym = 1
End If
End If
End Function
This will handle both an even or odd number of characters.
EDIT#1:
Simply:
Public Function Sym(s As String) As Long
Sym = 0
If s = StrReverse(s) Then Sym = 1
End
With Microsoft365, try:
Formula in B1:
=EXACT(A1,CONCAT(MID(A1,SEQUENCE(LEN(A1),,LEN(A1),-1),1)))
Formula in C1:
=--EXACT(A1,CONCAT(MID(A1,SEQUENCE(LEN(A1),,LEN(A1),-1),1)))
If you are working in a version without CONCAT() it will get significatly more verbose, but still possible:
=SUMPRODUCT(--EXACT(MID(A1,ROW(A$1:INDEX(A:A,LEN(A1))),1),MID(A1,(LEN(A1)+1)-ROW(A$1:INDEX(A:A,LEN(A1))),1)))=LEN(A1)
This, again, can be wrapped to return either 1 or 0 if you prefer that over the boolean results:
=--(=SUMPRODUCT(--EXACT(MID(A1,ROW(A$1:INDEX(A:A,LEN(A1))),1),MID(A1,(LEN(A1)+1)-ROW(A$1:INDEX(A:A,LEN(A1))),1)))=LEN(A1))
How to create an excel formula for the above equation?
Eg. N = 10, P = 9.4
Thanks!
Try the following user defined function:
Public Function Zigma(N As Long, p As Double) As Double
Dim i As Long
Zigma = N
For i = 1 To N - 1
Zigma = Zigma - (i / N) ^ p
Next i
End Function
This allows you to avoid array formulas.
Here it is as an excel formula:
=A1-SUMPRODUCT((ROW(A1:INDEX(A:A, A1-1))/A1)^A2)
Having a problem with this Error. I am creating a GA and the loop is to assign my fitness value to an array.
some of the variables
Dim Chromolength as integer
Chromolength = varchromolength * aVariables
Dim i as integer, j as integer, counter as integer
Dim Poparr() As Integer
Dim FitValarr() As Integer
the code:
ReDim Poparr(1 To PopSize, 1 To Chromolength)
For i = 1 To PopSize
For j = 1 To Chromolength
If Rnd < 0.5 Then
Poparr(i, j) = 0
Else
Poparr(i, j) = 1
End If
Next j
Next i
For i = 1 To PopSize
j = 1
counter = Chromolength
Do While counter > 0
FitValarr(i) = FitValarr(i) + Poparr(i, counter) * 2 ^ (j - 1)
j = j + 1
counter = counter - 1
Loop
Next i
I am having problems with:
FitValarr(i) = FitValarr(i) + Poparr(i, counter) * 2 ^ (j - 1)
I apologize, I am fairly new to VBA.
An overflow condition arises when you create an integer expression that evaluates to a value larger than can be expressed in a 16-bit signed integer. Given the expression, either the contents of FitValarr(i), or the expression 2^(j-1) could be overflowing. Suggest all the the variables presently declared as Int be changed to Long. Long integers are 32-bit signed values and provide a correspondingly larger range of possible values.
I had the same run time error 6. After much investigation l discovered that mine was a simple 'divide by zero' error.
I set up an integer value to hold Zip codes, and Error 6 events plagued me - until I realized that a zip code of 85338 exceeded the capacity of an int...
While I didn't think of a zip code as a "value" it was nonetheless certainly interpreted as one. I suspect the same could happen with addresses as well as other "non-numeric" numeric values. Changing the variable to a string resolved the problem.
It just didn't occur to me that a zip code was a "numeric value." Lesson learned.
I am currently working with arrays and loops, and am trying to write a function that will output an n by m array (a matrix) with the numbers {1, 2, 3, ... , n*m}
I am trying to learn some basic VBA code, this is purely for educational purposes.
This is what I have come up with:
Function createMatrix(n, m)
Dim matrix(1 To n, 1 To m) As Integer
x = 1
For i = 1 To n
For j = 1 To m
matrix(i, j) = x
x = (x + 1)
Next j
Next i
createMatrix = matrix
End Function
It returns #VALUE. I cannot understand why.
I got it to work at one point (creating a 3x3 matrix) by making it a function that did not take any variables and then initializing the matrix array by
Dim matrix(1 to 3, 1 to 3) As Integer
replacing n and m in the for loops with 3s.
So I guess the variables n and m are causing the problems, but don't know why.
Array declarations must be static (where the bounds are defined by a hardcoded value); however you can resize them dynamically using the ReDim statement.
' Declare an array.
' If you want to size it based on variables, do NOT define bounds.
Dim matrix() As Integer
' Resize dynamically.
ReDim maxtrix(n, m)
Note that when you ReDim, all values will be lost. If you had values in matrix that you wanted to keep, you can add the Preserve keyword:
ReDim Preserve matrix(n, m) ' Keep any existing values in their respective indexes.
You first need to declare array as dynamic array and then redim it to your dimension.
Function createMatrix(n, m)
Dim matrix() As Integer
ReDim matrix(1 To n, 1 To m) As Integer
x = 1
For i = 1 To n
For j = 1 To m
matrix(i, j) = x
x = (x + 1)
Next j
Next i
createMatrix = matrix
End Function
I am trying to find the largest prime divisor of a number x. When x is smaller than 1billion my code works but when it is greater than 1billion it gives an overflow error and debugging highlights the line with Mod in it.
Sub Largest_Divisor()
Dim x As Double
Dim Q As Integer
Q = 0
Dim L() As Double
x = 999999999#
Dim i As Double
For i = 775145 To 3 Step -2
If x Mod i = 0 Then
If IsPrime(i) Then
ReDim Preserve L(Q) As Double
L(Q) = i
Q = Q + 1
End If
End If
Next i
MsgBox (Application.Max(L))
End Sub
I suspect it is when x is larger than about 2 billion, 2,147,483,648 to be precise, that you have trouble.
That is because as per the documentation of mod, at most a long is returned, which ranges in value from -2,147,483,648 to 2,147,483,647 as a 32-bit signed value. It is not explicitly stated in the help documentation, but the arguments of mod are probably coerced to long as well.
A good work around can be to use this function:
Function Modulus(int1 As Double, int2 As Double) As Double
' This function will return int1 Mod int2. It is useful when |int1| exceeds
' 2,147,483,647 as the VBA Mod function will then break.
'
Dim myInt As Integer
myInt = Int(int1 / int2)
Modulus = int1 - (myInt * int2)
Return Modulus
End Function