Omit Digit Method Bug - java

I'm having an issue with a method that creates an array of consecutive digits (i.e. if you input 1 and 10 as an argument, the array will include every number from 1-10), and then compares each number to another digit (e.g. 4) - if the numbers match (e.g. 4 == 4), then it removes that number from the array. Finally it returns that array.
I've implemented the method below which works sometimes, but not all the time and I'm not sure why?
For example, if I created a new array and printed each array:
ArrayList<Integer> omittedDigitArray = new ArrayList<Integer>(Omit.allIntegersWithout(20, 45, 3));
System.out.println("Array - Numbers with Omitted Digit:");
for (int n : omittedDigitArray) {
System.out.print(n + ", ");
}
The number 29 is omitted from the array? Could anyone tell me why please? Thanks!
// Creates the ArrayList
ArrayList<Integer> numberList = new ArrayList<Integer>();
// Loop creates an array of numbers starting at "from" ending at "to"
for (int i = from; i < to + 1; i++) {
numberList.add(i);
}
// Check the array to see whether number contains digit
// Code checks whether x contains 5, n == one digit
// IMPORTANT: Doesn't work on the first half of numbers i.e / will remove 3 but not 30
for (int j = 0; j < numberList.size(); j++) {
int number = (int) numberList.get(j); // This can be any integer
int thisNumber = number >= 0 ? number: -number; // if statement in case argument is negative
int thisDigit;
while (thisNumber != 0) {
thisDigit = thisNumber % 10; // Always equal to the last digit of thisNumber
thisNumber = thisNumber / 10; // Always equal to thisNumber with the last digit chopped off, or 0 if thisNumber is less than 10
if (thisDigit == omittedDigit) {
numberList.remove(j);
j--;
}
}
}
// Return the completed Array list
return numberList;
}
}

Your inner loop has a problem. Once you remove an element from the list, you should break from that loop. Otherwise you might remove unrelated additional numbers (if the omitted digit appears several times in the same number).
while (thisNumber != 0) {
thisDigit = thisNumber % 10; // Always equal to the last digit of thisNumber
thisNumber = thisNumber / 10; // Always equal to thisNumber with the last digit chopped off, or 0 if thisNumber is less than 10
if (thisDigit == omittedDigit) {
numberList.remove(j);
j--;
break; // add this
}
}
I ran your code (+ my suggested fix) with a range of 1 to 50 and omitted digit 4 and got :
[1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 39, 50]
The problem in your code occurs when 44 is being processed - after you remove it (due to the first 4, you continue the while loop, find another 4 and remove another number, which happens to be 39, since the numbers 40 to 43 were already removed).

My suggested solution:
ArrayList<Integer> numberList = new ArrayList<Integer>();
// Loop creates an array of numbers starting at "from" ending at "to"
for (int i = from; i < to + 1; i++) {
numberList.add(i);
}
//removing all numbers that contain the digit
numberList.removeIf(j->containsDigit(j,thisDigit));
return numberList;
}
boolean containsDigit(int number,int thisDigit){
//making sure thisDigit is positive
thisDigit=Math.abs(thisDigit)
//if thisDigit is not a digit result is false
if(thisDigit>=10) return false;
//breaking the number into its digits
List<Integer> digits=new ArrayList<Integer>();
while(number>0){
digits.add(number%10);
number=(int) number/10;
}
return digits.contains(thisDigit);
}

Related

Get Immediate Successor in a Given Number Sequence

In an infinite sequence of numbers [2, 5, 7, 22, 25, 27, 52, 55, 57, 72, 75, 77, 222, ...].
Given any number in this sequence get the immediate successor number.
Example:
Input Output
22 25
77 222
5 7
I have written the below logic to find the next number in a sequence.
public static int getNextNumInSequence(Integer sequenceCurrentNum) {
List<Integer> sequence = new ArrayList<>();
sequence.add(2);
sequence.add(5);
sequence.add(7);
if(sequence.get(0).equals(sequenceCurrentNum))
return sequence.get(1);
else if(sequence.get(1).equals(sequenceCurrentNum))
return sequence.get(2);
//This is not a finite loop, just for my testing i am running 300 iterations.
for(int i=0;i<300;i++) {
if(sequence.get(i).equals(sequenceCurrentNum)) {
return sequence.get(i+1);
}
int nextVal = sequence.get(i)*10;
Integer firstSeq = nextVal + sequence.get(0);
Integer secondSeq = nextVal + sequence.get(1);
Integer thirdSeq = nextVal + sequence.get(2);
sequence.add(firstSeq);
sequence.add(secondSeq);
sequence.add(thirdSeq);
if(firstSeq.equals(sequenceCurrentNum)) {
return secondSeq;
}else if(secondSeq.equals(sequenceCurrentNum)) {
return thirdSeq;
}
}
return 0;
}
My Approach:
I am constructing the entire sequence from the beginning
Then checking if we have reached to the given number in sequence.
Then return the successor.
Drawbacks:
I am constructing the entire sequence to reach to given number.
Memory wise and performance wise not suggestable.
Please help to understand, is there any better approach to get the successor without constructing entire sequence.
Example: Given 277755 should return 277757. (Without constructing the
entire sequnce)
Note: The sequence will not be provided as an input to our function. The only input we will be given is a valid number from the sequence.
Try this.
public static int getNextNumInSequence(Integer sequenceCurrentNum) {
int head = sequenceCurrentNum / 10;
int tail = sequenceCurrentNum % 10;
int headNext = head == 0 ? 2 : getNextNumInSequence(head);
if (headNext == 0) return 0;
switch (tail) {
case 2: return head * 10 + 5;
case 5: return head * 10 + 7;
case 7: return headNext * 10 + 2;
default: return 0;
}
}
public static void main(String[] args) {
for (int i = 0, k = 2; i < 20; ++i, k = getNextNumInSequence(k))
System.out.println(i + " : " + k);
}
output:
0 : 2
1 : 5
2 : 7
3 : 22
4 : 25
5 : 27
6 : 52
7 : 55
8 : 57
9 : 72
10 : 75
11 : 77
12 : 222
13 : 225
14 : 227
15 : 252
16 : 255
17 : 257
18 : 272
19 : 275
You can also get n-th number.
public static int getNumAtIndex(int n) {
int h = n / 3;
int t = n % 3;
return (h == 0 ? 0 : getNumAtIndex(h) * 10)
+ (t == 0 ? 2 : t == 1 ? 5 : 7);
}
test:
public static void main(String[] args) {
for (int i = 0; i < 10; ++i)
System.out.println(i + " : " + getNumAtIndex(i));
}
output:
0 : 2
1 : 5
2 : 7
3 : 52
4 : 55
5 : 57
6 : 72
7 : 75
8 : 77
9 : 522
First try to understand what is the logic behind the sequence. If you look carefully to the numbers, you may see counting in ternary base. To be more clear, let's replace '2' by '0', '5' by '1' and '7' by '2'. Then your sequence becomes:
(0, 1, 2, 10, 11, 12, 20, 21, 22, 100, 101, 102, ...)
It's just counting.
So the thing is to get the next number in ternary base, but using the digits 2, 5, 7. We must take care of digit 7: if we increment it, we get 2 but we have a carry for the digit before.
Here is a sample code:
public static Integer getNextNumInSequence(Integer number)
{
int digits[] = {2,5,7};
int idx_digits[] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, -1};
Integer next_number = 0;
int carry = 1;
Integer pow10 = 1;
while (number>0)
{
int digit = number%10; //extract last digit
int idx_d = idx_digits[digit]; //get index of digit -- must be 0,1 or 2
if (idx_d==-1)
{
System.out.println("Invalid number");
return -1;
}
next_number += digits[(idx_d+carry)%3]*pow10; //compute next digit in sequence, taking care of the carry
carry = (digit==7)?1:0; //carry is 1 only if the current digit is 7
pow10 *= 10; //increment
number /= 10; //erase last digit
if (carry==0) //if no carry, we can stop the loop here, it's not useful to continue
{
break;
}
}
// at this point, either number>0 or carry==1
return ((carry>0)?2:number)*pow10+next_number; //final number is the digit sequence [2 if carry else number ; next_number]
}
You can solve this recursively.
If the final digit of the given number is 2 or 5, then it is easy: just change that final digit to 5 or 7 respectively.
Otherwise (when the final digit is 7), solve the problem without the last digit, and then append the digit 2 to that result. Of course, "without last digit" means an integer division by 10, and "appending" means multiplying by 10 and then adding the value of the digit.
Here is the function:
public static int getNextNumInSequence(Integer curr) {
if (curr % 10 == 2) return curr + 3;
if (curr % 10 == 5) return curr + 2;
if (curr == 7) return 22;
return getNextNumInSequence(curr / 10) * 10 + 2;
}
Note that one call has worst case time complexity of O(logn) where n is the value of the function argument, but amortised time complexity is O(1) per call.
To construct the list, you can simply do this:
List<Integer> list = Arrays.asList(2, 5, 7, 22, 25, 27, 52, 55, 57, 72, 75, 77, 222);
Note that there are cases where there is not successor. Here I will return null in those cases:
public static Integer getNextNumInSequence(List<Integer> list, Integer num) {
int pos = list.indexOf(num);
if (pos >= 0 && pos+1 < list.size()) {
return list.get(pos+1);
}
return null;
}
Note that I've added a parameter list so that you don't have to build the list each time you want to do a search.
In your example, the list is sorted; If it's always the case, you can use a binary search: Collections.binarySearch(list, num) instead of list.indexOf(num).
OK. If I understand correctly, you have three initial values:
static final int[] initial = {2, 5, 7};
and you can calculate the value at position ix like this:
private static int calculate(int ix) {
int div = ix/initial.length;
int rest = ix%initial.length;
int value = 0;
if (div > 0) {
value = 10*calculate(div-1);
}
return value+initial[rest];
}
To get the successor of num:
public static Integer getNextNumInSequence(int num) {
for (int i = 0; ; ++i) {
int cur = calculate(i);
if (cur == num) {
return calculate(i+1);
} else if (cur > num) {
return null;
}
}
}

Java int array find and print matching

Here I have a Java case switch where I compare 2 randomly generated 7 number integer arrays, lottery1 array is generated earlier depending on the user input. The problem I am having is, I need to compare the two arrays and count the number of matching numbers and then print the matching numbers and how many numbers were the same.
I'm trying to put the matching numbers into the array called similar, now it's just comparing the first number of lottery1 to all of the lottery2's numbers. There's plenty of tutorials on how to compare arrays that return a bool but I need the matching numbers, please help!
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
Random rnd = new Random();
int[] lottery1 = new int[7];
for (int i = 0; i < lottery1.length; i++) {
lottery1[i] = rnd.nextInt(52);
}
Arrays.sort(lottery1);
System.out.printf("Lottery array is: %s", Arrays.toString(lottery1));
System.out.print("\nDo you want to generate an array(y/n): ");
char answer = scan.next().charAt(0);
switch (answer) {
case 'n' -> System.out.print("Goodbye!");
case 'y' -> {
int[] lottery2 = new int[7];
for (int i = 0; i < lottery2.length; i++) {
int rndNum = rnd.nextInt(52);
lottery2[i] = rndNum; //Here i fill the lottery2 with random
} numbers
Arrays.sort(lottery2);
System.out.printf("Program created an array of: %s", Arrays.toString(lottery2));
int j = 0;
int[] similar = new int[7]; //I'm trying to put the matching numbers into this new array
for (int i = 0; i < 7; i++)
{
if (lottery2[i] == lottery1[j])
{
lottery1[i] = similar[j];
i++;
j++;
}
}
System.out.printf("\nThis is how many numbers are matching: ");
System.out.printf("\nThese numbers are matching ones: ");
}
I get that you are trying to compare all numbers in 2 list and get the ones with same values I wrote this code I think it answers your question:
int[] matching = new int[7];
int[] lottery1 = new int[7];
int[] lottery2 = new int[7];
// Generate random numbers
for (int i = 0; i < 7; i++) {
lottery1[i] = (int) (Math.random() * 52.0);
lottery2[i] = (int) (Math.random() * 52.0);
}
// Compare and store matching numbers in matching array;
// The nested loop below will compare the every element of the both lists
// together and store the
// results in matching array
int matchingCount = 0;
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 7; j++) {
if (lottery1[i] == lottery2[j]) {
matching[matchingCount++] = lottery1[i];
}
}
}
System.out.print("Matching Count: " + matchingCount + "\nMatch Numbers: [ ");
for (int i = 0; i < matchingCount; i++)
System.out.print(matching[i] + " ");
System.out.println("]");
int[] similar = new int[7]; //I'm trying to put the matching numbers into this new array
lottery1[i] = similar[j];
similar is an array of size 7, filled with seven 0 values (because arrays start out zeroed out). You never write anything into similar. You overwrite lottery1 with what's in similar. In other words, this code is an obtuse way to accomplish:
lottery1[i] = 0;
which surely isn't what you wanted. You also initialize similar to have size 7 but this is incorrect: Who knows how many similar numbers even exist?
You have 4 options:
Use a List<Integer> list = new ArrayList<Integer>(); - unlike arrays, arraylists don't need to be pre-sized, you can just call list.add(), and the list will take care of it. It's variable size.
Loop twice; once to determine how many matches, then you can make your similar array with the right size, and then a second time to fill it.
Make the similar array at 7, also count how many similar numbers exist, then when done copy the data over to a new array at the proper size.
Make the similar array at size 7 and use a special sentinel value (such as -1) to indicate that this one should not be shown/printed.
Also, your code is buggy: If you have replications, you overcount. Imagine lottery1 is [1,2,3,4,5,6,1] and lottery2 is [1,2,2,3,4,1,6], your algorithm would say that there are 6 matches, which doesn't sound right (the first '1' matches twice, the second '1' matches twice, and the 2 matches 2. You're going to have to think about how you want to tackle this issue.
Think about this, and write down some sample inputs + the sample output you desire, and then think about how to write code that does this. Don't just dive in.
This seems to be a good task to learn decomposition of the bigger tasks into subtasks.
The first subtask is definitely to generate an array of size K of random integer values in the given range, let's assume that for lottery the range is from 1 to N inclusive.
Then two arrays are generated, and the second subtask is to find a match between these two.
An example implementation using Stream API could be as follows:
Generate array of random integers:
static int[] getRandomArray() {
return getRandomArray(7, 52);
}
static int[] getRandomArray(int k, int n) {
int[] result = new SecureRandom().ints(1, n + 1) // n + 1 to ensure N can be included
.distinct() // make sure all elements are different
.limit(k) // pick K numbers
// .sorted() // sort the array if needed
.toArray();
System.out.println("Random array: " + Arrays.toString(result));
return result;
}
Match the results with the help of Set:
static int[] findMatch(int[] lotteryPick, int[] lotteryGuess) {
Set<Integer> set = Arrays.stream(lotteryPick).boxed().collect(Collectors.toSet());
int[] match = Arrays.stream(lotteryGuess).filter(x -> set.contains(x)).toArray();
if (match.length == 0) {
System.out.println("No matched numbers found");
} else {
String num = match.length == 1 ? " number" : " numbers";
System.out.println("Matched: " + match.length + num + ", the match: " + Arrays.toString(match));
}
System.out.println("-------------------------------");
return match;
}
Then the tests would look as simple as:
int t = 5;
while (t--> 0) {
findMatch(getRandomArray(), getRandomArray());
}
Possible output:
Random array: [26, 33, 29, 23, 49, 1, 14]
Random array: [37, 3, 27, 29, 34, 24, 36]
Matched: 1 number, the match: [29]
-------------------------------
Random array: [9, 4, 32, 27, 29, 18, 35]
Random array: [34, 2, 23, 29, 27, 6, 30]
Matched: 2 numbers, the match: [29, 27]
-------------------------------
Random array: [35, 18, 4, 42, 19, 6, 13]
Random array: [30, 8, 4, 37, 31, 9, 46]
Matched: 1 number, the match: [4]
-------------------------------
Random array: [52, 7, 47, 22, 12, 9, 26]
Random array: [46, 13, 20, 17, 1, 4, 34]
No matched numbers found
-------------------------------
Random array: [31, 40, 9, 3, 2, 49, 44]
Random array: [2, 15, 13, 36, 10, 43, 12]
Matched: 1 number, the match: [2]
-------------------------------

How would I find the first 5 multiples of 4, 5, and 6 and add them to an ArrayList?

I have a homework assignment that needs me to write a method that, given a list, will remove all multiples of 3 and add the first 5 multiples of 4, 5, and 6. It will then add that list to a HashSet, removing the duplicate integers, and return the size of the HashSet.
I've figured out everything else in this problem save for the "add first 5 multiples of 4, 5, and 6". My current code is the one I'm stuck with, using a for loop to iterate from 1 to 30. However, given an empty list, this adds 28, which is the 7th multiple of 4. I've tried nesting loops so I can iterate to 30 while at the same time iterating to 5, but none of my attempts have worked. Can anyone help me out?
Below is my current code.
public static int modify(List<Integer> list) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i) == null) {
throw new IllegalArgumentException("Cannot be null.");
}
if (list.get(i) % 3 == 0) {
list.remove(i);
}
}
for (int i = 1; i <= 30; i++) {
if (i % 4 == 0) {
list.add(i);
}
if (i % 5 == 0) {
list.add(i);
}
if (i % 6 == 0) {
list.add(i);
}
}
Collections.sort(list);
HashSet<Integer> unique = new HashSet<Integer>();
unique.addAll(list);
return unique.size();
}
Instead of counting to 30 and checking for incidences of multiples of 4, 5, and 6, why don't you find the multiples directly?
for (int i = 1; i <= 5; i++) {
list.add(4 * i);
list.add(5 * i);
list.add(6 * i);
}
If there are any duplicates, they'll be removed when you add them to the HashSet.
Side note: I'm not sure why you're bothering to sort the list before you add them to the HashSet. Sets inherently have no order so it doesn't matter if the list is sorted or not.
I'm assuming you're expecting the output of passing in an empty list to be:
unique = [4, 5, 6, 8, 10, 12, 15, 16, 18, 20, 24, 25, 30]
size = 13
With your current logic, there's nothing stopping it from adding multiples which are more than 5 (i.e. the 7th multiple of 4 for example), you're just continuing on until you've hit 30 in the loop. Instead, I might recommend having your for loop go 1-5 and then multiply by the factor you'd like a multiple of. i.e.:
// add the first 5 multiples of 4,5,6 to the list
for (int i = 1; i < 6; i++) {
list.add(i*4);
list.add(i*5);
list.add(i*6);
}
I rewrote the whole method making some changes:
The removal part of multiples of 3 in this way is more efficient. I
used the very useful removeIf() method.
I wrote the solution to your problem, identical to the other
answers.
CODE
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
for (int i = 0; i < 30; i++) {
list.add(i);
}
System.out.println(modify(list));
}
public static int modify(List<Integer> list) {
list.removeIf(n -> (n % 3 == 0));
for (int i = 1; i < 6; i++) {
list.add(4 * i);
list.add(5 * i);
list.add(6 * i);
}
Collections.sort(list);
HashSet<Integer> unique = new HashSet<>(list);
System.out.println(unique.toString()); // You can delete this
return unique.size();
}
OUTPUT
[1, 2, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 25, 26, 28, 29, 30]
26

Add the consecutive digits of a birthdate recursively till it reaches to a single digit

I am developing a numerology application which has to provide a result which is similar to the following,
1 5 0 8 1 9 9 4
6 5 8 9 1 1 1
1 1 1 1 2 2
2 2 2 3 4
4 4 5 7
8 9 1
1 1
2
It has to add the consecutive digits and retain the first digit if the sum is of 2 digits.
I am missing something. Adding a while loop for the length of intList doesn't seem to work.
int date;
List<Integer> sumList = new ArrayList<Integer>();
Scanner s = new Scanner(System.in);
System.out.println("Enter the date");
date = s.nextInt();
int len = Integer.toString(date).length();
int[] convertarray = new int[len];
for (int index = 0; index < len; index++) {
convertarray[index] = date % 10;
date /= 10;
}
List<Integer> intList = new ArrayList<Integer>();
for (int i : convertarray) {
intList.add(i);
}
Collections.reverse(intList);
System.out.println(intList);
int sum = 0;
int size = intList.size();
for (int i = 0; i < intList.size() - 1; i++) {
sum = intList.get(i) + intList.get(i + 1);
int length = (int) (Math.log10(sum) + 1);
if (length > 1) {
int firstDigit = Integer.parseInt(Integer.toString(sum).substring(0, 1));
sum = firstDigit;
}
System.out.print(sum + " ");
sumList.add(sum);
}
System.out.println("\n");
intList.clear();
intList = sumList;
My output is something like,
1 5 0 8 1 9 9 4
6 5 8 9 1 1 1
A simple recursive solution:
public static void main(String[] args) throws Exception {
String birthday = "01091995";
int[] digits = Arrays.stream(birthday.split("")).mapToInt(Integer::parseInt).toArray();
recursiveFunction(digits);
}
private static void recursiveFunction(int[] digits) {
if(digits.length == 1) {
// Base Case
System.out.println(digits[0]);
} else {
// Recursive Case
System.out.println(Arrays.toString(digits));
int[] digitsProcessed = new int[digits.length -1];
for (int i = 0; i < digits.length - 1; i++) {
digitsProcessed[i] = digits[i] + digits[i+1]; // Logic
}
recursiveFunction(digitsProcessed);
}
}
This produces:
[0, 1, 0, 9, 1, 9, 9, 5] // 8 numbers
[1, 1, 9, 10, 10, 18, 14] // 7 numbers
[2, 10, 19, 20, 28, 32] // 6 numbers
[12, 29, 39, 48, 60] // 5 numbers
[41, 68, 87, 108] // 4 numbers
[109, 155, 195] // 3 numbers
[264, 350] // 2 numbers
614 // 1 number
Adding a while loop for the length of intList doesn't seem to work.
Well it can be done with loops, but it would be harder and messier.
An algorithm with recursion would be the following:
Init the array of integers.
Call the recursive function "F" with the array.
From now, the recursive function behaviour:
Check if the recieved array's length is 1.
If it is, print the element and terminate.
If it is not:
Print the recieved array.
Make a new array.
Put in this new array the result of processing the recieved one adding as intended.
Call the recursive function "F" with this new array.

Given n and k, return the kth permutation sequence

The set [1,2,3,…,n] contains a total of n! unique permutations.
By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3 ) :
"123"
"132"
"213"
"231"
"312"
"321"
Given n and k, return the kth permutation sequence.
For example, given n = 3, k = 4, ans = "231".
There are multiple solutions out there. But all of them uses either factorial or there complexity is larger than O(n) such as O(n!). If you use factorial and find the number at the position by k/(n-1)!, the problem comes when n is large(n = 100). Here as n is large, (n-1)! overflows and becomes 0. In result, I am getting a divide by zero error...any solution or algorithm for that?
Here is my code:
public class KthPermutation {
public String getPermutation(int n, int k) {
// initialize all numbers
ArrayList<Integer> numberList = new ArrayList<Integer>();
for (int i = 1; i <= n; i++) {
numberList.add(i);
}
int fact = 1; // set factorial of n-1
for (int i = 1; i <= n-1; i++) {
fact = fact * i;
}
if ((long) k > (long) fact * n) {
k = (int) ((long) k - (long) (fact * n));
}
k--; // set k to base 0
StringBuilder result = new StringBuilder();
result = getP(result, numberList, n, k, fact);
return result.toString();
}
public static StringBuilder getP(StringBuilder result,
ArrayList<Integer> numberList, int n, int k, int fact) {
if (numberList.size() == 1 || n == 1) {
result.append(numberList.get(0));
return result; // return condition
}
int number = (k / fact) + 1 ;
result.append(numberList.get(number - 1));
numberList.remove(number - 1);
k = k % fact; // update k
fact = fact / (n - 1);
n--;
return getP(result, numberList, n, k, fact);
}
}
So if I'm reading the question correctly, you want to find the kth permutation, preferrably without using BigIntegers, provided k is not large enough to require a BigInteger.
If we look at the sequence
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
We can rewrite it so that the number in each position is an index into a list of the numbers that haven't appeared so far on the line:
0 0 0
0 1 0
1 0 0
1 1 0
2 0 0
2 1 0
So for example "2, 0, 0" means start with the list "1, 2, 3", then take the third (because we are indexing from zero), which is a 3, then take the first of the remaining digits "1, 2" which is a 1, then the first of the remaining digit, which is "2". So it produces "3, 1, 2".
To generate these indices, go from right to left and divide k by 1! for the rightmost two places, then 2! then 3! then 4! etc, and then modulo the result with the number of possible indices in that position, which is 1 for the rightmost, 2 for the second-rightmost etc. You don't have to calculate the factorial each time because you can keep a running product.
You can break out of the loop as soon as k divided by the factorial is zero, so you only have to compute factorials up until roughly the size of k multiplied by the last place in which k divided by the factorial is non-zero. If k is too large, you need to switch to BigIntegers.
Once you have the indices it's pretty straightforward to use them to generate the permutation.
Code (k starts from 0, so to find the first pass 0, not 1):
static public void findPermutation(int n, int k)
{
int[] numbers = new int[n];
int[] indices = new int[n];
// initialise the numbers 1, 2, 3...
for (int i = 0; i < n; i++)
numbers[i] = i + 1;
int divisor = 1;
for (int place = 1; place <= n; place++)
{
if((k / divisor) == 0)
break; // all the remaining indices will be zero
// compute the index at that place:
indices[n-place] = (k / divisor) % place;
divisor *= place;
}
// print out the indices:
// System.out.println(Arrays.toString(indices));
// permute the numbers array according to the indices:
for (int i = 0; i < n; i++)
{
int index = indices[i] + i;
// take the element at index and place it at i, moving the rest up
if(index != i)
{
int temp = numbers[index];
for(int j = index; j > i; j--)
numbers[j] = numbers[j-1];
numbers[i] = temp;
}
}
// print out the permutation:
System.out.println(Arrays.toString(numbers));
}
Demo
output:
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
10000000th permutation for n = 100:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 92, 98, 96, 90, 91, 100, 94, 97, 95, 99, 93]
The indices for k'th permutation (used in the answer to this question) are the factoradic representation of k and can be calculated without using factorial or running product.
public static List<Integer> toFactoradic(int x) {
List<Integer> result = new ArrayList<>();
for(int i = 1; x > 0; x /= i++) {
result.add(x % i);
}
Collections.reverse(result);
return result;
}
Of course, the indices array should be padded by 0's from the left so that length of the indices array is equal to number of elements to get the actual indices. Alternatively, the permutation could be applied from the right end.
Of course there is a need for bigints with such an interface
when you have n = 100 then you have n! permutations which means k is in the range k=<1,n!>
100!=93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
which does not fit into the standard unsigned int
2^32= 4294967296
2^64=18446744073709551616
see Fast exact bigint factorial
if you change the interface a bit you suddenly do not need any bigints anymore
just change API so it sequentially returns 1st,2nd,3th,...permutation without specifying k so you need something like:
Generalized Permutation (without repetitions) in C++
of course this is usable only if your usage of permutation is also sequential. You can also make function previous() to handle algorithms which are almost sequential. For random or non-sequential access you need to use bigints
First we can generate the factoradic reprsentation of k and then use it generate the necessary permutation. Please see https://en.wikipedia.org/wiki/Factorial_number_system for more details.
public String getPermutation(int n, int k) {
LinkedList<Integer> factoradic = new LinkedList<>();
k=k-1; // because factoradic representation and its mapping to permutation starts from 0
for(int i=1;i<=n; i++){ // get radix digits for n digits
factoradic.addFirst(k%i);
k=k/i;
}
//System.out.println(factoradic.size());
List<Integer> numbers = new LinkedList<>();
for(int i=1;i<=n;i++){
numbers.add(i);
}
StringBuilder str = new StringBuilder();
for(int x: factoradic){
// System.out.println(x);
str.append(String.valueOf(numbers.get(x)));
numbers.remove(x);
}
return str.toString();
}

Categories