0

I want to create a script that users can use to generate a pin with, following registration of an email. The pin needs to be 6 digits long and unique; no two users can have the same pin.

I have the following code, however haven't been able to progress beyond being stuck in an indefinite loop. As more pins are used, the probability of looping the while() function increases. Does any one have any idea of a more elegant solution to this?

Users use their pins to access free services from the website. There is no failure of the service if a user guesses another pin, but it would disrupt the user experience.

If possible, I'd like to distribute the PINs in such a way that statistically the probability of guessing a pin is negligable.

<?php
if($_POST['srSubmit'] && $_POST['srEmail'] && $_POST['srPass']) {
    $conn = mysqli_connect('localhost','root','','db_test');
    while(1) {
        $pin = rand(111111,999999);
        $sel = mysqli_query($conn,"SELECT * FROM formusers WHERE pin = '$pin'");
        if(mysqli_num_rows($sel) != 0) {    continue; }

        mysqli_query($conn,"INSERT INTO formusers(email,password,pin) VALUES('".$_POST['srEmail']."','".$_POST['srPass']."','".$pin."')");
        if(mysqli_affected_rows($conn)!=-1)  {
            echo "Pin:" . $pin;
            exit;
        } else {
            echo "Existing email, try again<br />";
        }
        break;
    }

}
?>
<form method="POST" action="">
<input type="email" name="srEmail" value="" placeholder="Email" /><br />
<input type="password" name="srPass" value="" placeholder="Password" /><br />
<input type="submit" name="srSubmit" value="Register" />
</form>
alias51
  • 8,178
  • 22
  • 94
  • 166
  • Sorry but how many reworded questions are you going to raise about how to create a random unique 6 digit number? – Anigel Jul 22 '13 at 19:15
  • It's updated code, and is about probability and distributing PINs, not how to use the while() function. – alias51 Jul 22 '13 at 19:17
  • 1
    Why not just use a password rather than an artificially restrictive set of possible answers, alterntaively you could just try and insert with a unique index on pin in your db which should be a fast query as it would be checking directly against a unique key, but as long as you rely on random number generation and a unique contraint there will always be more and more collisions as the number of entries grow – Anigel Jul 22 '13 at 19:18
  • I want to use the PIN so users can access telephone services. Part of the problem is how to distribute the generation of these PIN numbers, so users don't input a different (but say closely matched) PIN and the wrong response. – alias51 Jul 22 '13 at 19:21
  • 1
    Make an array with all possible pins, draw one random from the array and remove it from the array, repeat? – Joachim Isaksson Jul 22 '13 at 19:23
  • You mean if you give 123123 to one user, you don't want to give 123124 to someone else? This will make things even slower, because there's more likelihood of a collision. – Barmar Jul 22 '13 at 19:23
  • You will need to spend some time thinking what you actually want. I mean is 123456 closely matched to 123446, how many users you have to support and how different the pins need to be, If you really want to go that far, you need to manually select all the acceptable pins and just take one out of a db each time you assign it to someone – Anigel Jul 22 '13 at 19:23
  • How many difference does it take to make them not "closely matched"? Are 111111 and 222222 closely matched? – Barmar Jul 22 '13 at 19:25
  • I think Joachim has the best solution. Create an array of all possible PINS (thereby its possible to control the distribution) and then select one randomly, remove and repeat. This presumably has a much lighter load on the server? – alias51 Jul 22 '13 at 21:35

1 Answers1

0

I agree with some of the commenters that you might want to rethink your approach. However, if you decide to take it, and if you interpret closeness in terms of numerical distance (as opposed, for example, to Levenshtein distance), then you might consider using a Halton sequence or other quasi-random sequence. From Wikipedia:

The Halton sequence is constructed according to a deterministic method that uses a prime number as its base. As a simple example, let's take one dimension of the Halton sequence to be based on 2 and the other on 3. To generate the sequence for 2, we start by dividing the interval (0,1) in half, then in fourths, eighths, etc., which generates

1⁄2, 1⁄4, 3⁄4, 1⁄8, 5⁄8, 3⁄8, 7⁄8, 1⁄16, 9⁄16,...

To apply this idea to your situation, just multiply the sequence by your maximum PIN and take the floor. This approach won't generate collisions until you actually start to fill up your space and will disperse your PINS evenly. It would make it difficult for users to guess other users' PINs randomly so long as your number of users is much less than your number of PINs, but if someone finds out the sequence you're using they can of course reproduce the complete list.

jcrudy
  • 3,921
  • 1
  • 24
  • 31