Visual Basic biginteger giving me different solutions - vb.net

Im trying to make a program that indefinitely generates rows of Pascal's triangle. I was using integers, singles, etc but kept getting overflows and values of "Infinity" so I tried using biginteger and now it gives me different results. I am using visual studio 2012
This was my output for the first few with integer data type:
1 ,
1 , 1 ,
1 , 2 , 1 ,
1 , 3 , 3 , 1 ,
This was my output for the first few with biginteger data type:
1 ,
1 , 1 ,
1 , 2 , 0 ,
1 , 3 , 3 , 0 ,
The ONLY thing i have changed is the data type. Here is the code for the relevant bit:
n = rows
val(0) = 1
For k = 1 To rows
val(k) = val(k - 1) * (n / k)
n -= 1
Next
rows is the current row (I have this on a timer_tick so that it can run indefinitely and rows increases by 1 each tick)
How can I get the biginteger data type to return the same values as the integer data type?

EDITED as I realized stackoverflow didnt make your linebreaks, Integer is 32 bit while bigint is 64 bit. Fractions can get rounded in different directions when more digits are involved.
To easily avoid rounding errors, you could eliminate division from your code.
e.g.) Here we have a running string and temp string. We split the temp string to calculate the next row only using addition and subtraction.
Dim c As String = "1"
Dim temp As New StringBuilder
Dim f As New StringBuilder
temp.Append(c)
f.Append(temp.ToString)
For n = 1 To 10
Dim a() As String = c.Split(",")
For x = LBound(a) To UBound(a)
If UBound(a) - LBound(a) >= 1 Then
If x = UBound(a) Then
Dim v1 As UInt32 = a(x - 1)
Dim v2 As UInt32 = a(x)
temp.Append((v1) + (v2) & "," & 1)
c = temp.ToString
f.Append(vbCrLf & temp.ToString)
temp.Remove(0, temp.Length)
ElseIf x = LBound(a) Then
Dim v1 As UInt32 = a(x)
Dim v2 As UInt32 = a(x + 1)
temp.Append(1 & "," & (v1) + (v2) & ",")
ElseIf x < UBound(a) - 1 Then
Dim v1 As UInt32 = a(x)
Dim v2 As UInt32 = a(x + 1)
temp.Append((v1) + (v2) & ",")
End If
Else
temp.Remove(0, temp.Length)
temp.Append("1,1")
f.Append(vbCrLf & temp.ToString)
temp.Remove(0, temp.Length)
temp.Append("1,2,1")
f.Append(vbCrLf & temp.ToString)
c = temp.ToString
temp.Remove(0, temp.Length)
End If
Next
Next
MsgBox(f.ToString)

Related

"Out of memory" error for very large tridimensional array

I am trying to populate and then perform calculations based on a rather large array. This is to populate a binomial tree to calculate option price, so after populating the array I would need to perform repeated calculation so I'd prefer having 3 dimensions in my array for easy reference further down in the code. First dimension is the number of periods that have passed, second dimension is the number of price increases and the third is the number of price decreases.
Dim arr() As Double
Dim periods As Integer
Dim p As Long, i As Long
Dim u As Double, d As Double
Dim iniprice As Double
Let periods = 400
Let iniprice = 100
Let u = 1.1
Let d = 0.9
ReDim arr(0 To periods, 0 To periods, 0 To periods)
Let arr(0, 0, 0) = iniprice
For p = 1 To UBound(arr, 1)
For i = 0 To p
arr(p, i, p - i) = WorksheetFunction.RoundDown(arr(0, 0, 0) * u ^ i * d ^ (p - i), 2)
Next i
Next p
Is this a limitation stemming from the amount of RAM available on my PC (currently having 8Gb) or is this a limitation of VBA itself? Since one period is usually one day, a periods value of 1000 is normal (252 days = 1 trading year).
I also noticed that I have a lot of unused values, because I want to populate only values that have this format arr(p, i, p-i), values such as arr(10,10,10) will be 0. I'd greatly appreciate a workaround to this.
You can try using a single lookup column to represent the 3D array (not the best way but helps with the memory errors):
Dim periods As Integer
Dim p As Long, i As Long
Dim u As Double, d As Double
Dim iniprice As Double
Dim d1 As Long, d2 As Long, d3 As Long, someRow As Long
Dim fndRange As Range
Let periods = 400
Let iniprice = 100
Let u = 1.1
Let d = 0.9
' Col A will be your lookup in format "X-X-X", Col B will hold values
With Sheets("some sheet")
.Range("B1") = iniprice
For d1 = 0 To periods
For d2 = 0 To periods
For d3 = 0 To periods
.Range("A" & someRow).Value = d1 & "-" & d2 & "-" & d3
someRow = someRow + 1
Next
Next
Next
End With
For p = 1 To periods
For i = 0 To p
Set fndRange = Sheets("some sheet").Columns(1).Find(p & "-" & i & "-" & (p-1))
fndRange.Offset(0,1).Value = WorksheetFunction.RoundDown(iniprice * u ^ i * d ^ (p - i), 2)
Next i
Next p

Label a set of objects with (A->Z,AA->ZZ, AAA->ZZZ) in VBA

I have a set which has an unknown number of objects. I want to associate a label to each one of these objects. Instead of labeling each object with a number I want to label them with letters.
For example the first object would be labeled A the second B and so on.
When I get to Z, the next object would be labeled AA
AZ? then BA, BB, BC.
ZZ? then AAA, AAB, AAC and so on.
I'm working using Mapbasic (similar to VBA), but I can't seem to wrap my head around a dynamic solution. My solution assumes that there will be a max number of objects that the set may or may not exceed.
label = pos1 & pos2
Once pos2 reaches ASCII "Z" then pos1 will be "A" and pos2 will be "A". However, if there is another object after "ZZ" this will fail.
How do I overcome this static solution?
Basically what I needed was a Base 26 Counter. The function takes a parameter like "A" or "AAA" and determines the next letter in the sequence.
Function IncrementAlpha(ByVal alpha As String) As String
Dim N As Integer
Dim num As Integer
Dim str As String
Do While Len(alpha)
num = num * 26 + (Asc(alpha) - Asc("A") + 1)
alpha = Mid$(alpha, 2,1)
Loop
N = num + 1
Do While N > 0
str = Chr$(Asc("A") + (N - 1) Mod 26) & str
N = (N - 1) \ 26
Loop
IncrementAlpha = str
End Function
If we need to convert numbers to a "letter format" where:
1 = A
26 = Z
27 = AA
702 = ZZ
703 = AAA etc
...and it needs to be in Excel VBA, then we're in luck. Excel's columns are "numbered" the same way!
Function numToLetters(num As Integer) As String
numToLetters = Split(Cells(1, num).Address(, 0), "$")(0)
End Function
Pass this function a number between 1 and 16384 and it will return a string between A and XFD.
Edit:
I guess I misread; you're not using Excel. If you're using VBA you should still be able to do this will the help of an reference to an Excel Object Library.
This should get you going in terms of the logic. Haven't tested it completely, but you should be able to work from here.
Public Function GenerateLabel(ByVal Number As Long) As String
Const TOKENS As String = "ZABCDEFGHIJKLMNOPQRSTUVWXY"
Dim i As Long
Dim j As Long
Dim Prev As String
j = 1
Prev = ""
Do While Number > 0
i = (Number Mod 26) + 1
GenerateLabel = Prev & Mid(TOKENS, i, 1)
Number = Number - 26
If j > 0 Then Prev = Mid(TOKENS, j + 1, 1)
j = j + Abs(Number Mod 26 = 0)
Loop
End Function

EXPLAIN what this VB CODE means

Function convertToText(ByVal data As String) As String
Dim result As String = Nothing
Dim i As Integer = 0
Dim j As Integer = 0
For Each c As Char In data.ToCharArray
j *= 2
If c = "1"c Then j += 1
i += 1
If i = 8 Then
i = 0
result &= Chr(j)
j = 0
End If
Next
Return result
End Function
It converts binary to text but its a bit difficult for me to understand the logic behind it.
Someone please help.
The code seems to convert a text containing a binary number representing 8 bit character codes to a string containing these characters.
The for each loop loops over all binary digits ("0" or "1") of the input. The code of each result character is computed and after every 8 input characters the code is considered to be complete and the new character whose code was determined is added to the result (result &= Chr(j) is the same as result = result & Chr(j). Chr(j) converts an Integer containing a character code into a character). The variable i counts the bits.
The variable j holds the character code. If a bit is "1", then 1 is added to j (j += 1 is the same as j = j + 1), but not if it is "0".
A "1" in the right most bit position has a (decimal) value of 1. The next to its left a value of 2. The next 4 and so on. The value doubles for each position until it reaches 128 for the left most bit of an 8 bit number. Therefore j is doubled on each loop (j *= 2 is the same as j = j * 2).
Example with just 4 bits:
data = "1010"
The binary number 1010 means
1 * 8 + 0 * 4 + 1 * 2 + 0 * 1 = (decimal)10
The code does this
j = 0 => 0
j *= 2 => 0
j += 1 => 1 'since c = "1"
j *= 2 => 2
'no += 1 since c = "0"
j *= 2 => 4
j += 1 => 5 'since c = "1"
j *= 2 => 10
'no += 1 since c = "0"
The first 1 we added is doubled 3 times and becomes 8. The second 1 we added is doubled only once and becomes 2. 8 + 2 = 10.

How to compare Strings for Percentage Match using vb.net?

I am banging my head against the wall for a while now trying different techniques.
None of them are working well.
I have two strings.
I need to compare them and get an exact percentage of match,
ie. "four score and seven years ago" TO "for scor and sevn yeres ago"
Well, I first started by comparing every word to every word, tracking every hit, and percentage = count \ numOfWords. Nope, didn't take into account misspelled words.
("four" <> "for" even though it is close)
Then I started by trying to compare every char in each char, incrementing the string char if not a match (to count for misspellings). But, I would get false hits because the first string could have every char in the second but not in the exact order of the second. ("stuff avail" <> "stu vail" (but it would come back as such, low percentage, but a hit. 9 \ 11 = 81%))
SO, I then tried comparing PAIRS of chars in each string. If string1[i] = string2[k] AND string1[i+1] = string2[k+1], increment the count, and increment the "k" when it doesn't match (to track mispellings. "for" and "four" should come back with a 75% hit.) That doesn't seem to work either. It is getting closer, but even with an exact match it is only returns 94%. And then it really gets screwed up when something is really misspelled. (Code at the bottom)
Any ideas or directions to go?
Code
count = 0
j = 0
k = 0
While j < strTempName.Length - 2 And k < strTempFile.Length - 2
' To ignore non letters or digits '
If Not strTempName(j).IsLetter(strTempName(j)) Then
j += 1
End If
' To ignore non letters or digits '
If Not strTempFile(k).IsLetter(strTempFile(k)) Then
k += 1
End If
' compare pair of chars '
While (strTempName(j) <> strTempFile(k) And _
strTempName(j + 1) <> strTempFile(k + 1) And _
k < strTempFile.Length - 2)
k += 1
End While
count += 1
j += 1
k += 1
End While
perc = count / (strTempName.Length - 1)
Edit: I have been doing some research and I think I initially found the code from here and translated it to vbnet years ago. It uses the Levenshtein string matching algorithm.
Here is the code I use for that, hope it helps:
Sub Main()
Dim string1 As String = "four score and seven years ago"
Dim string2 As String = "for scor and sevn yeres ago"
Dim similarity As Single =
GetSimilarity(string1, string2)
' RESULT : 0.8
End Sub
Public Function GetSimilarity(string1 As String, string2 As String) As Single
Dim dis As Single = ComputeDistance(string1, string2)
Dim maxLen As Single = string1.Length
If maxLen < string2.Length Then
maxLen = string2.Length
End If
If maxLen = 0.0F Then
Return 1.0F
Else
Return 1.0F - dis / maxLen
End If
End Function
Private Function ComputeDistance(s As String, t As String) As Integer
Dim n As Integer = s.Length
Dim m As Integer = t.Length
Dim distance As Integer(,) = New Integer(n, m) {}
' matrix
Dim cost As Integer = 0
If n = 0 Then
Return m
End If
If m = 0 Then
Return n
End If
'init1
Dim i As Integer = 0
While i <= n
distance(i, 0) = System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)
End While
Dim j As Integer = 0
While j <= m
distance(0, j) = System.Math.Max(System.Threading.Interlocked.Increment(j), j - 1)
End While
'find min distance
For i = 1 To n
For j = 1 To m
cost = (If(t.Substring(j - 1, 1) = s.Substring(i - 1, 1), 0, 1))
distance(i, j) = Math.Min(distance(i - 1, j) + 1, Math.Min(distance(i, j - 1) + 1, distance(i - 1, j - 1) + cost))
Next
Next
Return distance(n, m)
End Function
Did not work for me unless one (or both) of following are done:
1) use option compare statement "Option Compare Text" before any Import declarations and before Class definition (i.e. the very, very first line)
2) convert both strings to lowercase using .tolower
Xavier's code must be correct to:
While i <= n
distance(i, 0) = System.Math.Min(System.Threading.Interlocked.Increment(i), i - 1)
End While
Dim j As Integer = 0
While j <= m
distance(0, j) = System.Math.Min(System.Threading.Interlocked.Increment(j), j - 1)
End While

What could be slowing down my Excel VBA Macro?

This function goes through all integers and picks out binary values with only five ones and writes them to the spreadsheet.
To run this For x = 1 To 134217728 would take 2.5 days!!!! Help!
How could I speed this up?
Function D2B(ByVal n As Long) As String
n = Abs(n)
D2B = ""
Do While n > 0
If n = (n \ 2) * 2 Then
D2B = "0" & D2B
Else
D2B = "1" & D2B
n = n - 1
End If
n = n / 2
Loop
End Function
Sub mixtures()
Dim x As Long
Dim y As Integer
Dim fill As String
Dim mask As String
Dim RowOffset As Integer
Dim t As Date
t = Now
fill = ""
For x = 1 To 134217728
mask = Right(fill & CStr(D2B(x)), Len(fill & CStr(D2B(x))))
Debug.Print mask
If x > 100000 Then Exit For
If Len(mask) - Len(WorksheetFunction.Substitute(mask, "1", "")) = 5 Then _
RowOffset = RowOffset + 1
For y = 1 To Len(mask)
If Len(mask) - Len(WorksheetFunction.Substitute(mask, "1", "")) = 5 Then _
Range("mix").Offset(RowOffset).Cells(y) = Mid(mask, y, 1)
Next
Next
Debug.Print DateDiff("s", Now, t)
End Sub
By first sight guess, I think the problem lies in the fact that you do that cell by cell, which causes many read and write accesses.
You should do it range by range, like
vArr = Range("A1:C1000").Value
' it is array now, do something here effeciently
Range("A1:C1000").Value = vArr
You want find all 28bit numbers with 5 1s
There are 28*27*26*25*24/5/4/3/2=98280 such numbers
The following code took ~10 seconds on my PC:
lineno = 1
For b1 = 0 To 27
For b2 = b1 + 1 To 27
For b3 = b2 + 1 To 27
For b4 = b3 + 1 To 27
For b5 = b4 + 1 To 27
Cells(lineno, 1) = 2 ^ b1 + 2 ^ b2 + 2 ^ b3 + 2 ^ b4 + 2 ^ b5
lineno = lineno + 1
Next
Next
Next
Next
Next
mask = Right(fill & CStr(D2B(x)), Len(fill & CStr(D2B(x))))
The above line of code does the same thing (CStr(D2B(x))) twice.
Store the result of CStr(D2B(x)) in a variable & use that variable in the above line of code.
I've got 2 suggestions:
Get rid of the substitution command by counting the ones/zeroes in D2B and return an empty string if the count does not equal 5
Write these pre-filtered bitstrings to an array first and copy the array directly to the cells when finished.
Something like
ws.Range(ws.cells(1, 1), ws.cells(UBound(dstArr, 1) + 1, UBound(dstArr, 2) + 1)) = dstArr
The array-copy-trick greatly improves performance!