My Assistant
![]() ![]() |
|
|
Dec 27 2008, 03:37 AM
Post
#1
|
|
|
UtterAccess Guru Posts: 932 From: South Africa |
Hi
I am trying to write some code that will determine valid 5-digit PIN numbers. The only rules for a PIN number are:
Without these rules, there are 100,000 possible PIN numbers, 00000 to 99999. My end goal is to determine how many possible PIN numbers are available after the rules have been applied. My knowledge of combinatorics has failed me on this, so I figured it could be done in a loop of sorts. Any help much appreciated. |
|
|
|
Dec 27 2008, 03:49 AM
Post
#2
|
|
|
Rent-an-Admin Posts: 8,769 From: Banana Republic |
It would probably be something like:
CODE For i = 0 to Len(s) If Mid(1, s) = Mid(1, s+1) Then If Mid(1, s = Mid(1, s+2) Then Msgbox "You can't have more than two identical digits in a row." End If End If If Mid(1, s) = Mid(1, s+1) - 1 Then Msgbox "You can't have consecutive digits." End If Next You could then append the number that pass the test to a temporary table if you need the values again, or just increment a value. HTH |
|
|
|
Dec 27 2008, 03:52 AM
Post
#3
|
|
|
Utterly Banned Posts: 6,006 From: Brisbane Qld Australia |
Sounds like an interesting task, but should not be too difficult to write.
However It may take a little time to run. First of all you will need 5 different variables. One for each digit. You need this to get around the problem of leading Zeros which cannot be represented as an integer. Your loop will need to incriment each of these Variables. Then a Case statement to handle the various scenarios. Now it is starting to get complicated. Let's see what others think. There may be a better way, but i think I am on the right track. Please let me know how things progress. |
|
|
|
Dec 27 2008, 03:55 AM
Post
#4
|
|
|
Utterly Banned Posts: 6,006 From: Brisbane Qld Australia |
Banana
That was my first thought but it does not take into account a number starting with Zero. What do you think about my suggestion? Namely the 5 different Vatiables. Dim A as Integer A = 0 Dim B etc |
|
|
|
Dec 27 2008, 04:09 AM
Post
#5
|
|
|
Rent-an-Admin Posts: 8,769 From: Banana Republic |
Well, my method assumed a string, rather than a number and implicitly typecast (though I probably should have had added CLng() to be explicit where I was doing addition) the individual string to a number for consecutive digit check. Comparsion should work whether it's a string or a number.
I, however expect my method to have few errors (e.g. when the loop reaches 2nd to last digit, the comparison will fail because there's nothing beyond the Mid(1, i+2), but left it up to the OP to implement the error handling. I hope it will work but if my brain is addled (and I'm pretty sure it is), we can try the Select Case with five variables. Edited by: BananaRepublic on Sat Dec 27 4:17:28 EST 2008. |
|
|
|
Dec 27 2008, 04:16 AM
Post
#6
|
|
|
Utterly Banned Posts: 6,006 From: Brisbane Qld Australia |
Let's see what the OP comes up with.
|
|
|
|
Dec 27 2008, 04:49 AM
Post
#7
|
|
|
Retired Moderator Posts: 11,289 From: Milwaukee, WI |
Some clarification on the requirements are needed:
True/False - this is a valid PIN: 44144 but these are not: 44544, 44554 What about decreasing numbers? 12345 is not allowed. Is 54321 a valid PIN? I like the approach of separating the 5 digits into individual Long variables. You could then run code like this: CODE ' ' check for unique PIN ' Public Function IsPinUnique(nPIN As Long) As Boolean Dim nCopyOfPin As Long Dim arrLongs(4) As Long Dim x As Long nCopyOfPin = nPIN IsPinUnique = True ' assume success ' assign ints For x = 0 To 4 arrLongs(x) = nCopyOfPin \ (10 ^ (4 - x)) nCopyOfPin = nCopyOfPin - (arrLongs(x) * (10 ^ (4 - x))) Next x For x = 0 To 4 If x > 0 Then If (arrLongs(x - 1) = (arrLongs(x) - 1)) Then IsPinUnique = False Exit For End If End If If x > 1 Then ' have enough digits to look for consecutive appearances of a digit If ((arrLongs(x) = arrLongs(x - 1)) And (arrLongs(x) = arrLongs(x - 2))) Then IsPinUnique = False Exit For End If End If Next x End Function I'm no math major so there certainly may be a smarter solution but this seems to work... |
|
|
|
Dec 27 2008, 05:50 AM
Post
#8
|
|
|
Utterly Banned Posts: 6,006 From: Brisbane Qld Australia |
John
What you have may in fact be correct. However may I suggest that you place some comments so that the OP understands what you have done. I think you can read between the lines here. |
|
|
|
Dec 27 2008, 06:11 AM
Post
#9
|
|
|
Utter A-fishin'-ado Posts: 17,723 From: Cincinnati, Ohio, USA . . . ><((((°> |
Oooo ... it's like 7th grade math class all over again! Can I play? I ain't no number-genius like John but I *think* I may have another solution ... don't hold your breath too long though. Let me see what I can come up with ...
|
|
|
|
Dec 27 2008, 06:31 AM
Post
#10
|
|
|
Utter A-fishin'-ado Posts: 17,723 From: Cincinnati, Ohio, USA . . . ><((((°> |
Would someone be so kind as to double-check this for validity?
CODE Public Function ValidatePin(Pin As String) As Boolean
Dim PinArray(4) As Long Dim x As Long 'assume success ValidatePin = True 'assign array For x = 0 To 4 PinArray(x) = CLng(CStr(x + 1) & Mid(Pin, x + 1, 1)) Next x 'check for consecutive digits For x = 0 To 3 If PinArray(x + 1) - PinArray(x) = 11 Then ValidatePin = False End If Next x 'check for triple digits For x = 0 To 2 If PinArray(x + 1) - PinArray(x) = 10 And PinArray(x + 2) - PinArray(x + 1) = 10 Then ValidatePin = False End If Next x End Function |
|
|
|
Dec 27 2008, 06:54 AM
Post
#11
|
|
|
Utter A-fishin'-ado Posts: 17,723 From: Cincinnati, Ohio, USA . . . ><((((°> |
Well, I tested both John's function as well as the one I posted. They are functionally equivalent. As far as who's is more efficient, I'll let someone else ponder that one. (IMG:http://www.utteraccess.com/forum/style_emoticons/default/cool.gif)
I did change John's first line: Public Function IsPinUnique(nPIN As Long) As Boolean ...to... Public Function IsPinUnique(nPIN As String) As Boolean ...so I could pass in "00000" and other leading zero numbers. I think Access will coerce them for you behind the scenes, but I just wanted to clarify the input. Interestingly, out of 100000 possible combinations, ***** fail validation. (Deleted by Walter in case this is some classwork problem) Edited by: niesz on Sat Dec 27 6:59:39 EST 2008. |
|
|
|
Dec 27 2008, 07:30 AM
Post
#12
|
|
|
Utterly Banned Posts: 6,006 From: Brisbane Qld Australia |
Walter
I am not surprised to see you in on this one. I also expected Chris to put in his 2 cents worth. What you have written is just part of the solution is it not. We need to check every number from 0 (which would be represented as 000000) to 999999 So the passing of the value Pin as string is not what is required here. You need to increase the values for each loop. Am I right. |
|
|
|
Dec 27 2008, 07:39 AM
Post
#13
|
|
|
Utter A-fishin'-ado Posts: 17,723 From: Cincinnati, Ohio, USA . . . ><((((°> |
>>We need to check every number from 0 (which would be represented as 000000) to 999999
So the passing of the value Pin as string is not what is required here.<< I would argue that 00000 is not a number. But yes, in the grand scheme of things, the OP wants to find out how many combinations are good -vs- bad. The way I handled it was to insert 100000 entries (of strings of text) into a table, added another column for the result, and then ran an UPDATE query against the function. Then you can easily filter on all the False results to see the bad combinations. But it just seemed like too much of an intellectual exercise to not be an academic question. So I removed the result I came up with. |
|
|
|
Dec 27 2008, 12:47 PM
Post
#14
|
|
|
Rent-an-Admin Posts: 8,769 From: Banana Republic |
QUOTE Would someone be so kind as to double-check this for validity? FWIW, your code looks good but that's my eyeballing. I didn't understand the point of the line- CODE PinArray(x) = CLng(CStr(x + 1) & Mid(Pin, x + 1, 1)) , which I believe you're trying to store the position along with the value? I may be missing something, but I don't see needing position for the calculation. I see one point though with the consecutive digits test- if a PIN has say, 13448, this will fail because the code hits ValidatePin = False once in the loops when the OP would say that this is perfectly fine. (14448, OTOH isn't). Also, I would like to point out that this is a eager evaluation and thus will take longer because everything will get tested, even if the test failed the first time. To implement a lazy evaluation, we may want to try something like this:CODE Public Function ValidatePin(Pin As String) As Boolean Dim PinArray(4) As Long Dim x As Long 'assume failure (Note: Boolean variable are false by default but left in line for readability) ValidatePin = False 'assign array For x = 0 To 4 PinArray(x) = CLng(Mid(Pin, x + 1, 1) Next x 'check for consecutive digits For x = 0 To 3 If PinArray(x + 1) - PinArray(x) = 0 Then If PinArray(x + 2) - PinArray(x) = 0 Then 'Short-circuit Exit Function End If End If Next x 'check for triple digits For x = 0 To 2 If PinArray(x + 1) - PinArray(x) = 0 Then If PinArray(x + 2) - PinArray(x + 1) = 0 Then 'Short-Circuit Exit Function End If End If Next x 'Passed the validation. ValidatePin = True End Function Just my two cents' worth. |
|
|
|
Dec 27 2008, 01:40 PM
Post
#15
|
|
|
Utter A-fishin'-ado Posts: 17,723 From: Cincinnati, Ohio, USA . . . ><((((°> |
Hi Banana,
>>if a PIN has say, 13448, this will fail because the code hits ValidatePin = False once in the loops when the OP would say that this is perfectly fine.<< 13448 *should* fail because of the "34" combination. This would constitute two consecutive numbers. I posted your code into a module to try it out ... This line: PinArray(x) = CLng(Mid(Pin, x + 1, 1) ...has unbalanced parens and immediately threw a Compile error. After I fixed that and ran the code it threw Subscript Out Of Range errors and I had to terminate it. I think I understand what you meant to post and have changed the function to short-circuit as you mentioned ... also did not include the digit position increment. CODE Public Function ValidatePin(Pin As String) As Boolean
Dim PinArray(4) As Long Dim x As Long 'assign array For x = 0 To 4 PinArray(x) = CLng(Mid(Pin, x + 1, 1)) Next x 'check for consecutive digits For x = 0 To 3 If PinArray(x + 1) - PinArray(x) = 1 Then Exit Function End If Next x 'check for triple digits For x = 0 To 2 If PinArray(x + 1) - PinArray(x) = 0 And PinArray(x + 2) - PinArray(x + 1) = 0 Then Exit Function End If Next x ValidatePin = True End Function I am not a great fan of multiple exit points, but it does work. (IMG:http://www.utteraccess.com/forum/style_emoticons/default/cool.gif) ... I suppose Brent will be along shortly with a byte-array version. (IMG:http://www.utteraccess.com/forum/style_emoticons/default/laugh.gif) |
|
|
|
Dec 27 2008, 02:11 PM
Post
#16
|
|
|
Rent-an-Admin Posts: 8,769 From: Banana Republic |
Well, that's what happen when I write VBA code in UtterAccess reply box, not in VBE!
I have to agree with you that I don't really like multiple exit points (oh how I wish VBA had Return instead of Function = Assignment), but the idea is to speed things up with lazy evaluation. Why bother with the rest of tests if it's a failure at the first test? Also, to point out this: CODE If PinArray(x + 1) - PinArray(x) = 0 And PinArray(x + 2) - PinArray(x + 1) = 0 Then is actually a eager evaluation because both sides get evaluated, regardless of the fact that if one side fails, both will fails anyway. This is why I used a more wordy version: CODE If PinArray(x + 1) - PinArray(x) = 0 Then If PinArray(x + 2) - PinArray(x + 1)= 0 Then so it becomes lazy again. VBA doesn't have OrElse and And<Else?> constructs available in other language to do what I had to do with two nested Ifs. Oh well. I wouldn't be surprised to see Brent two-up us. (IMG:http://www.utteraccess.com/forum/style_emoticons/default/laugh.gif) |
|
|
|
Dec 27 2008, 03:45 PM
Post
#17
|
|
|
UtterAccess Guru Posts: 932 From: South Africa |
In answer to mishj's question:
44144 is valid. 44544, 44554 are not valid |
|
|
|
Dec 27 2008, 03:49 PM
Post
#18
|
|
|
UtterAccess Guru Posts: 932 From: South Africa |
Wow I didn't expect such an involved response, thanks all for the feedback, it will take me a little while to go through it all.
This most definitely does sound like a classwork problem (but it's not :-)), and in fact it will make an excellent combinatorics question I think. Apart from being a useful utility to validate PIN no's, I want to show my bank that their policy on choosing PIN no's is seriously flawed because it eliminates xxxx number of PIN numbers and actually makes it easier to guess a PIN no. |
|
|
|
Dec 27 2008, 11:00 PM
Post
#19
|
|
|
Retired Moderator Posts: 11,289 From: Milwaukee, WI |
OK - I get 66250 combos in < .3 seconds. Can anyone confirm that number?
CODE '
' get the number of possible PINs ' ' Results: ' ' ? HowManyUniquePins() ' Count change in 1 second: 239403 ' QueryPerformanceCounter minimum resolution: 1/2394030000 sec (4.17705709619345E-10) ' There are 66250 unique combinations. ' Elapsed time: 0.2952 ' Public Function HowManyUniquePins() As Long Init_ExactTimer Dim nCopyOfPin As Long Dim arrLongs(4) As Long Dim x As Long, y As Long Dim bFailed As Boolean HowManyUniquePins = 0 DoCmd.Hourglass True For y = 0 To 99999 bFailed = False nCopyOfPin = y ' assign ints by doing integer division with powers of ten For x = 0 To 4 arrLongs(x) = nCopyOfPin \ (10 ^ (4 - x)) nCopyOfPin = nCopyOfPin - (arrLongs(x) * (10 ^ (4 - x))) Next x For x = 0 To 4 ' look for consecutive sequences - skip the first digit If x > 0 Then If (arrLongs(x - 1) = (arrLongs(x) - 1)) Then bFailed = True Exit For End If End If ' look for consecutive repeats - skip the first two digits If x > 1 Then ' have enough digits to look for consecutive appearances of a digit If (arrLongs(x) = arrLongs(x - 1)) Then If (arrLongs(x) = arrLongs(x - 2)) Then bFailed = True Exit For End If End If End If Next x 'Debug.Print y If bFailed = False Then HowManyUniquePins = HowManyUniquePins + 1 Next y Debug.Print "There are " & HowManyUniquePins & " unique combinations." DoCmd.Hourglass False Debug.Print "Elapsed time: " & ExactTimer_Value End Function |
|
|
|
Dec 28 2008, 04:46 AM
Post
#20
|
|
|
UtterAccess Guru Posts: 932 From: South Africa |
Here is the code from my solution, I just put it in a button OnClick event but will put in a proper function at some point:
I wrote a program to generate the output of all 100,000 pin no's to a text file, and mark which is valid or invalid, I zipped it and attached it if anyone wants to see. I get 66,250 valid and 33,750 invalid pins. Thanks all for the help. CODE Dim PinNo As String
Dim A As Integer, B As Integer, C As Integer, D As Integer, E As Integer PinNo = Me.PinNo 'Get the pin and validate as necessary to ensure 5-digit number 'Initialize variables A = 0 B = 0 C = 0 D = 0 E = 0 A = CInt(Mid(PinNo, 1, 1)) 'Extract the digit from the string - PinNo B = CInt(Mid(PinNo, 2, 1)) C = CInt(Mid(PinNo, 3, 1)) D = CInt(Mid(PinNo, 4, 1)) E = CInt(Mid(PinNo, 5, 1)) 'First look for consecutive numbers 'Second look for three like digits in a row If (((A - B) = -1) Or ((B - C) = -1) Or ((C - D) = -1) Or ((D - E) = -1)) Or _ (((A = B) And (B = C)) Or ((B = C) And (C = D)) Or ((C = D) And (D = E))) Then 'Invalid PIN MsgBox "Invalid PIN.", vbCritical Else 'Valid PIN MsgBox "Valid PIN", vbInformation End If
Attached File(s)
|
|
|
|
![]() ![]() |
|
Go to Top · Lo-Fi Version | Time is now: 22nd May 2013 - 02:41 PM |