Generate random number with different digits (digits will be as many as the number the user entered) in Java

Viewed 515

I want to generate a random number with as many digits as the user enters. All of these digits should be different from each other. I can't do it with arrays because I learned Java in methods, so what should I do? I thought of doing it this way but up to k == 10 is too long. Is there a shorter way? If the user enters the value of a as 8, I have to create a number with 8 different digits.

public static void main(String[] args) {
    Scanner scn = new Scanner(System.in);
    System.out.println("a: ");
    int a = scn.nextInt();
    random(a);
}

public static void random(int k) {
    Random rand = new Random();
    int rand;
    if (k == 1) {
        rand = 1 + (int) (Math.random() * 9);
        System.out.println(rand);
    } else if (k == 2) {
        rand = 10 + (int) (Math.random() * 90);
        int digit1 = rand / 10;
        int digit2 = rand % 10;
        while (digit1 == digit2) {
            if (digit1 == 0) {
                digit2 = (int) (Math.random() * 9);
            }
        }
        System.out.println(rand);
    } else if (k == 3) {
        rand = 100 + (int) (Math.random() * 900);
        int digit1 = rand / 100;
        int digit2 = (rand / 10) % 10;
        int digit3 = rand % 10;
        while (digit1 == digit2 || digit1 == digit3 || digit2 == digit3) {
            if (digit1 == digit2 || digit2 == digit3) {
                digit2 = (int) (Math.random() * 9);
            }
            if (digit1 == digit3 || digit2 == digit3) {
                digit3 = (int) (Math.random() * 9);
            }
        }
        System.out.println(rand);
    }
}
3 Answers

Using Characters to allow a simple concatenation of the required digits. Then, if needed, you can convert it to numeric. 3 steps:


1. Store the numbers in a List:

List<Character> numbers = Arrays.asList('0','1','2','3','4','5','6','7','8','9');

2. Shuffle the list using a Random instance previously declared and initialized.

Edit - previously I told about creating a new Random() to pass as argument to the suffle method. As @chrylis-cautiouslyoptimistic correctly points on the comment, this is not necessary and just worsens the performance. Besides, as the comment also states, creating multiple instances in a short period of time will lead to repeated sequences, as the random generator relies on the system clock. So just reuse the same instance:

/*on variables declaration*/
final Random r = new Random();
...

Collections.shuffle(numbers, r);

3. Get the required digits, by concatenating the Characters.

Finally, parse the resulting String to numeric. I assume that (>0 requiredDigits <11)

Beware of leading zeros. If the first choosen character is 0, the parseInt method will logically remove it, returning a numeric value with an incorrect number of required digits. If more than a digit is requested, turn the leading zero into a trailing zero, by "moving" it to the end of the string. If just one digit is required parseInt will return 0, so no issues in that case.

 String result="";

 for (int i=0;i<requiredDigits;i++)
    result+=numbers.get(i);

 if (requiredDigits>1 && result.charAt(0)==('0'))
      result = result.replace("0", "") +'0';

int numericResult = Integer.parseInt(result);

Resume

You avoid constant calls to random number generations, the only "heavy" operation being the list shuffle. As its size is small, this operation will also be a really cheap one. This approach avoids constant numeric operations and works with a very limited list of decimal digits stored as Characters.

An optimization in order to avoid half of the shuffle random operations would be using one of these for even requests (2nd, 4th,...):

1. Collections.reverse(numbers);

2. Iterate the previously shuffled list in reverse order

    for (int i=requiredDigits-1; i>=0; i--)
        result+=numbers.get(i);

This way, only one shuffle operation would be enough for 2 requests.

Since you said you can't use arrays, I am assuming you can't use Lists either since they are similar. So here is a possible alternative.

  • Initialize a string to the digits.
  • generate a random number based on the string length.
  • get the digit at the location and add to the final value.
  • remove the digit from the string using substring methods.
  • continue until 8 digits have been selected.
for (int i = 0; i < 10; i++) {
    System.out.println(getUnique(8));
}

Prints something like this.

41985372
68942750
42978135
67829345
24970361
17209568
14759862
52083769
63420871
75631049

The generating method.

        
public static long getUnique(int ndigits) {
    if (ndigits < 1 || ndigits > 10) {
            throw new IllegalArgumentException(
                    "Number of digits must be between 1 and 10 inclusive");
    }
    String digits = "0123456789";
    long num = 0;
    for (int i = 0; i < ndigits; i++) {
        int d = (int) (Math.random() * digits.length());
        // make certain first digit is not a 0.
        // otherwise, number will only have `ndigits - 1` digits.
        if (d == 0 && i == 0) {
            i--;
            continue;
        }
        // convert character to an int and "append" to the num.
        num = num * 10 + digits.charAt(d) - '0';

        // effectively delete the just used digit from the string.
        digits = digits.substring(0, d) + digits.substring(d + 1);
    }
    return num;
}

Additional explanation of the statement num = num * 10 + digits.charAt(d) - '0';

  • initialize num to 0
  • say the character is a '3'. That is 063 in octal.
  • subtract '0' from it to get an int value of 3.
  • first time thru num = num * 10 + 3 = 3.
  • next digit is a '4' - '0' = 4
  • num = num * 10 + 4 = 3 * 10 + 4 = 34.
  • next digit is a '5'-'0' = 5
  • num = num * 10 + 5 = 34 * 10 + 5 = 345. continue in this fashion.
import java.util.Random;

class random{
    public static void main( String args[] ) {
    Random rand = new Random();
    int upperbound = 10;
    String s_val = "";
    int i = 0;
    int user_req_len = 10;
    int val;
    while(s_val.length()<user_req_len){
        val = rand.nextInt(upperbound); 
        if((s_val.indexOf(String.valueOf(val))==-1)){
            s_val = s_val+""+val;
        }
    }
    long value;
    value = Long.parseLong(s_val);
    System.out.println(value);
    }
}

This will work

Related