UtterAccess.com
X   Site Message
(Message will auto close in 2 seconds)

Welcome to UtterAccess! Please ( Login   or   Register )

Custom Search
2 Pages V  1 2 >  (Go to first unread post)
   Reply to this topicStart new topic
> Calculating 5-digit PIN numbers.    
 
   
b0b1
post Dec 27 2008, 03:37 AM
Post#1



Posts: 932
Joined: 13-January 04
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:
  • There cannot be more than 2 of the same digits in a row e.g. 14446 is not allowed, but 44146 is.
  • There cannot be any consecutive numbers e.g. 12345 and 12468 are not allowed.

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.
Go to the top of the page
 
BananaRepublic
post Dec 27 2008, 03:49 AM
Post#2


Dungeon Cleaner
Posts: 1,504
Joined: 16-June 07
From: Banana Republic


It would probably be something like:
!--c1-->
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
Go to the top of the page
 
Rainlover
post Dec 27 2008, 03:52 AM
Post#3


Utterly Banned
Posts: 6,006
Joined: 5-October 05
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.
Go to the top of the page
 
Rainlover
post Dec 27 2008, 03:55 AM
Post#4


Utterly Banned
Posts: 6,006
Joined: 5-October 05
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
Go to the top of the page
 
BananaRepublic
post Dec 27 2008, 04:09 AM
Post#5


Dungeon Cleaner
Posts: 1,504
Joined: 16-June 07
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.
Go to the top of the page
 
Rainlover
post Dec 27 2008, 04:16 AM
Post#6


Utterly Banned
Posts: 6,006
Joined: 5-October 05
From: Brisbane Qld Australia


Let's see what the OP comes up with.
Go to the top of the page
 
mishej
post Dec 27 2008, 04:49 AM
Post#7


Retired Moderator
Posts: 11,289
Joined: 25-September 02
From: Milwaukee, WI


Some clarification on the requirements are needed:
rue/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...
Go to the top of the page
 
Rainlover
post Dec 27 2008, 05:50 AM
Post#8


Utterly Banned
Posts: 6,006
Joined: 5-October 05
From: Brisbane Qld Australia


John
That 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.
Go to the top of the page
 
niesz
post Dec 27 2008, 06:11 AM
Post#9


Utter A-fishin'-ado
Posts: 17,958
Joined: 1-August 05
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 ...
Go to the top of the page
 
niesz
post Dec 27 2008, 06:31 AM
Post#10


Utter A-fishin'-ado
Posts: 17,958
Joined: 1-August 05
From: Cincinnati, Ohio, USA . . . ><((((°>


Would someone be so kind as to double-check this for validity?
!--c1-->
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
Go to the top of the page
 
niesz
post Dec 27 2008, 06:54 AM
Post#11


Utter A-fishin'-ado
Posts: 17,958
Joined: 1-August 05
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. cool.gif

Odid 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.
Go to the top of the page
 
Rainlover
post Dec 27 2008, 07:30 AM
Post#12


Utterly Banned
Posts: 6,006
Joined: 5-October 05
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.
Go to the top of the page
 
niesz
post Dec 27 2008, 07:39 AM
Post#13


Utter A-fishin'-ado
Posts: 17,958
Joined: 1-August 05
From: Cincinnati, Ohio, USA . . . ><((((°>


>>We need to check every number from 0 (which would be represented as 000000) to 999999
o 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.
Go to the top of the page
 
BananaRepublic
post Dec 27 2008, 12:47 PM
Post#14


Dungeon Cleaner
Posts: 1,504
Joined: 16-June 07
From: Banana Republic


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:
!--c1-->
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.
Go to the top of the page
 
niesz
post Dec 27 2008, 01:40 PM
Post#15


Utter A-fishin'-ado
Posts: 17,958
Joined: 1-August 05
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.

Oposted 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
nbsp;   
    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. cool.gif

... I suppose Brent will be along shortly with a byte-array version. laugh.gif
Go to the top of the page
 
BananaRepublic
post Dec 27 2008, 02:11 PM
Post#16


Dungeon Cleaner
Posts: 1,504
Joined: 16-June 07
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.
Owouldn't be surprised to see Brent two-up us. laugh.gif
Go to the top of the page
 
b0b1
post Dec 27 2008, 03:45 PM
Post#17



Posts: 932
Joined: 13-January 04
From: South Africa


In answer to mishj's question:
44144 is valid.
44544, 44554 are not valid
Go to the top of the page
 
b0b1
post Dec 27 2008, 03:49 PM
Post#18



Posts: 932
Joined: 13-January 04
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.
Go to the top of the page
 
mishej
post Dec 27 2008, 11:00 PM
Post#19


Retired Moderator
Posts: 11,289
Joined: 25-September 02
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
nbsp;   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
Go to the top of the page
 
b0b1
post Dec 28 2008, 04:46 AM
Post#20



Posts: 932
Joined: 13-January 04
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:
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.
Oget 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
HE = 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)
Attached File  PinNo.zip ( 16.32K )Number of downloads: 10
 
Go to the top of the page
 
2 Pages V  1 2 >


Custom Search
RSSSearch   Top   Lo-Fi    18th December 2017 - 07:56 AM