Fast way to check if a number is evenly divisible by another? - vb.net

I was wondering what the fastest way is to check for divisibility in VB.NET.
I tried the following two functions, but I feel as if there are more efficient techniques.
Function isDivisible(x As Integer, d As Integer) As Boolean
Return Math.floor(x / d) = x / d
End Function
Another one I came up with:
Function isDivisible(x As Integer, d As Integer) As Boolean
Dim v = x / d
Dim w As Integer = v
Return v = w
End Function
Is this a more practical way?

Use Mod:
Function isDivisible(x As Integer, d As Integer) As Boolean
Return (x Mod d) = 0
End Function

Use 'Mod' which returns the remainder of number1 divided by number2. So if remainder is zero then number1 is divisible by number2.
e.g.
Dim result As Integer = 10 Mod 5 ' result = 0

use the mod operator

Related

VBA: Testing for perfect cubes

I'm trying to write a simple function in VBA that will test a real value and output a string result if it's a perfect cube. Here's my code:
Function PerfectCubeTest(x as Double)
If (x) ^ (1 / 3) = Int(x) Then
PerfectCubeTest = "Perfect"
Else
PerfectCubeTest = "Flawed"
End If
End Function
As you can see, I'm using a simple if statement to test if the cube root of a value is equal to its integer portion (i.e. no remainder). I tried testing the function with some perfect cubes (1, 8, 27, 64, 125), but it only works for the number 1. Any other value spits out the "Flawed" case. Any idea what's wrong here?
You are testing whether the cube is equal to the double supplied.
So for 8 you would be testing whether 2 = 8.
EDIT: Also found a floating point issue. To resolve we will round the decimals a little to try and overcome the issue.
Change to the following:
Function PerfectCubeTest(x As Double)
If Round((x) ^ (1 / 3), 10) = Round((x) ^ (1 / 3), 0) Then
PerfectCubeTest = "Perfect"
Else
PerfectCubeTest = "Flawed"
End If
End Function
Or (Thanks to Ron)
Function PerfectCubeTest(x As Double)
If CDec(x ^ (1 / 3)) = Int(CDec(x ^ (1 / 3))) Then
PerfectCubeTest = "Perfect"
Else
PerfectCubeTest = "Flawed"
End If
End Function
#ScottCraner correctly explains why you were getting incorrect results, but there are a couple other things to point out here. First, I'm assuming that you are taking a Double as input because the range of acceptable numbers is higher. However, by your implied definition of a perfect cube only numbers with an integer cube root (i.e. it would exclude 3.375) need to be evaluated. I'd just test for this up front to allow an early exit.
The next issue you run into is that 1 / 3 can't be represented exactly by a Double. Since you're raising to the inverse power to get your cube root you're also compounding the floating point error. There's a really easy way to avoid this - take the cube root, cube it, and see if it matches the input. You get around the rest of the floating point errors by going back to your definition of a perfect cube as an integer value - just round the cube root to both the next higher and next lower integer before you re-cube it:
Public Function IsPerfectCube(test As Double) As Boolean
'By your definition, no non-integer can be a perfect cube.
Dim rounded As Double
rounded = Fix(test)
If rounded <> test Then Exit Function
Dim cubeRoot As Double
cubeRoot = rounded ^ (1 / 3)
'Round both ways, then test the cube for equity.
If Fix(cubeRoot) ^ 3 = rounded Then
IsPerfectCube = True
ElseIf (Fix(cubeRoot) + 1) ^ 3 = rounded Then
IsPerfectCube = True
End If
End Function
This returned the correct result up to 1E+27 (1 billion cubed) when I tested it. I stopped going higher at that point because the test was taking so long to run and by that point you're probably outside of the range that you would reasonably need it to be accurate.
For fun, here is an implementation of a number-theory based method described here . It defines a Boolean-valued (rather than string-valued) function called PerfectCube() that tests if an integer input (represented as a Long) is a perfect cube. It first runs a quick test which throws away many numbers. If the quick test fails to classify it, it invokes a factoring-based method. Factor the number and check if the multiplicity of each prime factor is a multiple of 3. I could probably optimize this stage by not bothering to find the complete factorization when a bad factor is found, but I had a VBA factoring algorithm already lying around:
Function DigitalRoot(n As Long) As Long
'assumes that n >= 0
Dim sum As Long, digits As String, i As Long
If n < 10 Then
DigitalRoot = n
Exit Function
Else
digits = Trim(Str(n))
For i = 1 To Len(digits)
sum = sum + Mid(digits, i, 1)
Next i
DigitalRoot = DigitalRoot(sum)
End If
End Function
Sub HelperFactor(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)
n = n / q ^ k
If n > 1 Then HelperFactor n, 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 HelperFactor n, 3, factors
Set factor = factors
End Function
Function PerfectCubeByFactors(n As Long) As Boolean
Dim factors As Collection
Dim f As Variant
Set factors = factor(n)
For Each f In factors
If f(1) Mod 3 > 0 Then
PerfectCubeByFactors = False
Exit Function
End If
Next f
'if we get here:
PerfectCubeByFactors = True
End Function
Function PerfectCube(n As Long) As Boolean
Dim d As Long
d = DigitalRoot(n)
If d = 0 Or d = 1 Or d = 8 Or d = 9 Then
PerfectCube = PerfectCubeByFactors(n)
Else
PerfectCube = False
End If
End Function
Fixed the integer division error thanks to #Comintern. Seems to be correct up to 208064 ^ 3 - 2
Function isPerfectCube(n As Double) As Boolean
n = Abs(n)
isPerfectCube = n = Int(n ^ (1 / 3) - (n > 27)) ^ 3
End Function

Overflow error VBA

I have this code below, and I'm getting an overflow error at the line:
s = s + (x Mod 10) [first line in the Do Loop]
Why? I declared x and s to be of type Double. Adding two doubles, why is this not working?
Thanks for your help.
Public Sub bidon1()
Dim i As Double, x As Double, s As Double, k As Byte, h As Byte
Dim y(1 To 6) As Double
For i = 1 To 1000000
x = i ^ 3
Do
s = s + (x Mod 10)
x = x \ 10
Loop Until x = 0
If s = x Then
k = k + 1
y(k) = x
If y(6) > 0 Then
For h = 1 To 6
Debug.Print y(h)
Next
Exit Sub
End If
End If
Next
End Sub
The problem is that the VBA mod operator coerces its arguments to be integers (if they are not already so). It is this implicit coercion which is causing the overflow. See this question: Mod with Doubles
On Edit:
Based on your comments, you want to be able to add together the digits in a largish integer. The following function might help:
Function DigitSum(num As Variant) As Long
'Takes a variant which represents an integer type
'such as Integer, Long or Decimal
'and returns the sum of its digits
Dim sum As Long, i As Long, s As String
s = CStr(num)
For i = 1 To Len(s)
sum = sum + Val(Mid(s, i, 1))
Next i
DigitSum = sum
End Function
The following test sub shows how it can be used to correctly get the sum of the digits in 999999^3:
Sub test()
Dim x As Variant, y As Variant
Debug.Print "Naive approach: " & DigitSum(999999 ^ 3)
y = CDec(999999)
x = y * y * y
Debug.Print "CDec approach: " & DigitSum(x)
End Sub
Output:
Naive approach: 63
CDec approach: 108
Since 999999^3 = 999997000002999999, only the second result is accurate. The first result is only the sum of the digits in the string representation of the double 999999^3 = 9.99997000003E+17

Rounding up to nearest higher integer in VBA

I'm trying to calculate how many layers a commodity will be stacked in. I have a variable quantity (iQty), a given width for the loadbed (dRTW), a width per unit for the commodity (dWidth) and a quantity per layer (iLayerQty).
The quantity per layer is calculated as iLayerQty = Int(dRTW/dWidth)
Now I need to divide the total quantity by the quantity per layer and round up. In an Excel formula it would be easy, but I'm trying to avoid WorksheetFunction calls to minimise A1/R1C1 confusion. At the moment I'm approximating it with this:
(Number of layers) = ((Int(iQty / iLayerQty) + 1)
And that works fine most of the time - except when the numbers give an integer (a cargo width of 0.5 m, for instance, fitting onto a 2.5 m rolltrailer). In those instances, of course, adding the one ruins the result.
Is there any handy way of tweaking that formula to get a better upward rounding?
I don't see any reason to avoid WorksheetFunction; I don't see any confusion here.
Number_of_layers = WorksheetFunction.RoundUp(iQty / iLayerQty, 0)
You could also roll your own function:
Function RoundUp(ByVal Value As Double)
If Int(Value) = Value Then
RoundUp = Value
Else
RoundUp = Int(Value) + 1
End If
End Function
Call it like this:
Number_of_layers = RoundUp(iQty / iLayerQty)
If using a WorksheetFunction object to access a ROUNDUP or CEILING function is off the table then the same can be accomplished with some maths.
Number of layers = Int(iQty / iLayerQty) - CBool(Int(iQty / iLayerQty) <> Round(iQty / iLayerQty, 14))
A VBA True is the equivalent of (-1) when used mathematically. The VBA Round is there to avoid 15 digit floating point errors.
I use -int(-x) to get the ceiling.
?-int(-1.1) ' get ceil(1.1)
2
?-int(1.1) ' get ceil(-1.1)
-1
?-int(-5) ' get ceil(5)
5
These are the functions I put together for this purpose.
Function RoundUp(ByVal value As Double) as Integer
Dim intVal As Integer
Dim delta As Double
intVal = CInt(value)
delta = intVal - value
If delta < 0 Then
RoundUp = intVal + 1
Else
RoundUp = intVal
End If
End Function
Function RoundDown(ByVal value As Double) as Integer
Dim intVal As Integer
Dim delta As Double
intVal = CInt(value)
delta = intVal - value
If delta <= 0 Then
RoundDown = intVal
ElseIf delta > 0 Then
RoundDown = intVal - 1
End If
End Function
This is my Ceiling in VBA.
Function Ceiling(ByVal Number As Double, ByVal Significance As Double) As Double
Dim intVal As Long
Dim delta As Double
Dim RoundValue As Double
Dim PreReturn As Double
If Significance = 0 Then
RoundValue = 1
Else
RoundValue = 1 / Significance
End If
Number = Number * RoundValue
intVal = CLng(Number)
delta = intVal - Number
If delta < 0 Then
PreReturn = intVal + 1
Else
PreReturn = intVal
End If
Ceiling = PreReturn / RoundValue
End Function

VB find out number is mod of which number

I'm trying to find the number that gives me the result, here's the equation:
x=y mod z
y=?
In this equation, I know values of x and z but I need to find of y too, does anyone has an idea?
There are many solutions to your problem because mapping integers to values in ring Zz (z integer numbers: 0, 1, ..., z - 1) is not a bijection:
y = { x + n * z : n is integer }
So, the simplest function that will provide you with an answer can be as simple as that:
Function solution( ByVal x As Integer) As Integer
Return x
End Function
You can also write something that will return you next possible solution:
Function solution_next( ByVal x As Integer, ByVal z As Integer) As Integer
Static n As Integer = 0
i + z * n
n += 1
Return i
End Function
You can adjust this further given more conditions.

Round an integer number in VB.NET

How could I round an integer number based on the last digit of the number?
For example:
Dim x As Integer = 12
Dim y As Integer = 139
Dim z As Integer = 2322
The result should be:
x = 20
y = 140
z = 2330
Use:
Math.Ceiling(value / 10) * 10
reference: http://msdn.microsoft.com/en-us/library/zx4t0t48.aspx#Y0
x = Math.Ceiling(x / 10.0) * 10
Module Module1
Public Function RoundUp(ByVal val As Double, ByVal pos As Integer) As Double
Dim base10 As Double = System.Math.Pow(10, pos) 'pos +: right from float point, -: left from float point.
If val > 0 Then
Return System.Math.Ceiling(val * base10) / base10
Else
Return System.Math.Floor(val * base10) / base10
End If
End Function
Sub Main()
System.Console.WriteLine(RoundUp(12, -1)) '20
System.Console.WriteLine(RoundUp(139, -1)) '140
System.Console.WriteLine(RoundUp(2322, -1)) '2330
System.Console.WriteLine(RoundUp(3.1415926, 3)) '3.142
System.Console.ReadKey()
End Sub
End Module
As an alternative, doing this subtraction is a fast way:
x = value + ((2200000000 - value) % 10)
Considering that value is int (2200000000 > int.MaxValue):