允许重复时如何获得第n个排列?
How to get nth permutation when repetition is allowed?
欧拉计划 24:数字 0、1、2、3、4、5、6、7、8 和 9 的百万分之一字典排列是什么?
如果允许重复怎么办?像 1111111111
、1223344457
等。如何获得百万次排列,其中重复也包括在计数中。
请注意,输入仍然相同。输入无重复。
我想生成所有可能的长度为 10 的密码。密码可以包含重复的字符,所以我希望我的函数也适用于此。
这是给出字符串第 n 次排列的代码。它的工作原理是利用 n 个元素有 n!排列。并在字典序排列中排在第一位 (n-1)!排列将从第一个数字开始,依此类推。
我如何修改它以获取重复的字符串?我应该使用什么特定算法?
澄清一下,我不仅需要百万次排列。我需要所有可能的排列。通过 运行 这个函数的 for 循环,我可以得到所有排列而不重复。但我无法通过重复获得排列。我想要重复排列。因为我想获得所有可能的密码。想一想如果只允许使用数字,您可以拥有 10 个字母的所有可能密码。 10^10。我想要所有的。
import java.util.*;
public class NthPermutation{
private static int Factorial(int n){
if (n < 0)
return 0;
int ans = 1;
for (int i=1;i<=n;++i)
ans *= i;
return ans;
}
public static String getNth(List<Integer> original, int permNum){
List<Integer> numbers = new ArrayList<Integer>(original);
String nth = "";
permNum--;
int N = numbers.size();
for (int i=1;i<N;++i){
int j = permNum / Factorial(N - i);
permNum = permNum % Factorial(N - i);
nth = nth + numbers.get(j);
numbers.remove(j);
if (permNum==0)
break;
}
for (int i=0; i<numbers.size();i++)
nth = nth + numbers.get(i);
return nth;
}
public static void main(String[] args){
List<Integer> numbers = new ArrayList<Integer>();
for (int i = 0; i < 10; i++)
numbers.add(i);
System.out.println(getNth(numbers,1000000));
}
}
如果允许重复,则:
- 第一个排列是
0000000000
- 第二个排列是
0000000001
- 第十个排列是
0000000009
- 百次排列是
0000000099
- 千次排列是
0000000999
- 百万次排列是
0000999999
等等。
所有这些只是数字 n-1
在左侧填充了足够数量的零以使整个字符串长度为 10。
因此,要获得实际的第 n 个组合,您所要做的就是(在 Python 中的代码段下方,您可以轻松转换为 Java):
>>> def find_nth_combination(n):
... print "0" * (10-len(str(n-1))) + str(n-1)
...
>>> find_nth_combination(1)
0000000000
>>> find_nth_combination(100)
0000000099
>>> find_nth_combination(9062300000)
9062299999
>>> find_nth_combination(12300000)
0012299999
如果你想通过重复来解决这个问题,你可以have a look here(代码在Python)。
要获得所有排列,只需遍历所有数字。
因此,您需要执行以下操作:
for x in xrange(1, 1001):
find_nth_combination(x)
这将输出:
0000000000
0000000001
...
...
0000000997
0000000998
0000000999
没有重复:
因为你只需要第1000000000次排列,你可以暴力破解这个问题。从字符串开始:“0123456789”并使用 C++ 等效的 next_permuation,您可以在此处获取函数:http://codeforces.ru/blog/entry/3980。只要迭代一百万次,你就会得到解决方案。
有一个更高级的贪婪解决方案,运行速度非常快,您可以在这里阅读:https://math.stackexchange.com/questions/60742/finding-the-n-th-lexicographic-permutation-of-a-string
有重复:
这个问题很简单,有重复,因为它等同于只取数字并在开头用 0 填充它。
1st permuation: 000000001
15th permuation 000000015
etc.
我们需要先了解Factoradic Number System(或Factoradic number system)来解决这道题。阶乘数字系统使用阶乘值而不是数字的幂(二进制系统使用 2 的幂,十进制使用 10 的幂)来表示位值(或基数)。
位值(基数)为 –
5!= 120 4!= 24 3!=6 2!= 2 1!=1 0!=1 等等。
第零位的数字始终为 0。第一位的数字(基数 = 1!)可以是 0 或 1。第二位的数字(基数为 2!)可以是 0,1 或 2,依此类推在。一般来说,第n位的数字可以取0-n之间的任意值。
前几个数字表示为因数-
0 -> 0 = 0*0!
1 -> 10 = 1*1! + 0*0!
2 -> 100 = 1*2! + 0*1! + 0*0!
3 -> 110 = 1*2! + 1*1! + 0*0!
4 -> 200 = 2*2! + 0*1! + 0*0!
5 -> 210 = 2*2! + 1*1! + 0*0!
6 -> 1000 = 1*3! + 0*2! + 0*1! + 0*0!
7 -> 1010 = 1*3! + 0*2! + 1*1! + 0*0!
8 -> 1100 = 1*3! + 1*2! + 0*1! + 0*0!
9 -> 1110
10-> 1200
字符串的第 n 个字典顺序排列与其因子表示之间存在直接关系。
例如,这里是字符串“abcd”的排列。
0 abcd 6 bacd 12 cabd 18 dabc
1 abdc 7 badc 13 cadb 19 dacb
2 acbd 8 bcad 14 cbad 20 dbac
3 acdb 9 bcda 15 cbda 21 dbca
4 adbc 10 bdac 16 cdab 22 dcab
5 adcb 11 bdca 17 cdba 23 dcba
如果仔细观察,我们可以在这里看到一个规律。第一个字母在每 6 次(3!)次排列后发生变化。第二个字母在 2(2!) 次排列后发生变化。第三个字母在每次(1!)排列后改变,第四个字母在每次(0!)排列后改变。我们可以利用这个关系直接找到第n个排列。
一旦我们用因数表示法表示 n ,我们就会考虑其中的每个数字,并将给定字符串中的一个字符添加到输出中。如果我们需要找到“abcd”的第 14 个排列。 14 in factoradics -> 2100.
从第一个数字开始->2,字符串为‘abcd’。假设索引从 0 开始,从字符串中取出位置 2 的元素并将其添加到输出。
Output String
c abd
2 012
下一个数字 -> 1.String 现在是“abd”。再次,提取位置 1 的字符并将其添加到输出。
Output String
cb ad
21 01
下一个数字 -> 0。字符串是“ad”。将位置 1 的字符添加到输出。
Output String
cba d
210 0
下一个数字 -> 0。字符串是“d”。将位置 0 处的字符添加到输出。
Output String
cbad ''
2100
要将给定的数字转换为阶乘数字系统,请依次将数字除以 1、2、3、4、5 等等,直到商变为零。每一步的余数构成因式表示。
例如,要将 349 转换为因式,
Quotient Remainder Factorial Representation
349/1 349 0 0
349/2 174 1 10
174/3 58 0 010
58/4 14 2 2010
14/5 2 4 42010
2/6 0 2 242010
349 的因子表示是 242010。
欧拉计划 24:数字 0、1、2、3、4、5、6、7、8 和 9 的百万分之一字典排列是什么?
如果允许重复怎么办?像 1111111111
、1223344457
等。如何获得百万次排列,其中重复也包括在计数中。
请注意,输入仍然相同。输入无重复。
我想生成所有可能的长度为 10 的密码。密码可以包含重复的字符,所以我希望我的函数也适用于此。
这是给出字符串第 n 次排列的代码。它的工作原理是利用 n 个元素有 n!排列。并在字典序排列中排在第一位 (n-1)!排列将从第一个数字开始,依此类推。
我如何修改它以获取重复的字符串?我应该使用什么特定算法?
澄清一下,我不仅需要百万次排列。我需要所有可能的排列。通过 运行 这个函数的 for 循环,我可以得到所有排列而不重复。但我无法通过重复获得排列。我想要重复排列。因为我想获得所有可能的密码。想一想如果只允许使用数字,您可以拥有 10 个字母的所有可能密码。 10^10。我想要所有的。
import java.util.*;
public class NthPermutation{
private static int Factorial(int n){
if (n < 0)
return 0;
int ans = 1;
for (int i=1;i<=n;++i)
ans *= i;
return ans;
}
public static String getNth(List<Integer> original, int permNum){
List<Integer> numbers = new ArrayList<Integer>(original);
String nth = "";
permNum--;
int N = numbers.size();
for (int i=1;i<N;++i){
int j = permNum / Factorial(N - i);
permNum = permNum % Factorial(N - i);
nth = nth + numbers.get(j);
numbers.remove(j);
if (permNum==0)
break;
}
for (int i=0; i<numbers.size();i++)
nth = nth + numbers.get(i);
return nth;
}
public static void main(String[] args){
List<Integer> numbers = new ArrayList<Integer>();
for (int i = 0; i < 10; i++)
numbers.add(i);
System.out.println(getNth(numbers,1000000));
}
}
如果允许重复,则:
- 第一个排列是
0000000000
- 第二个排列是
0000000001
- 第十个排列是
0000000009
- 百次排列是
0000000099
- 千次排列是
0000000999
- 百万次排列是
0000999999
等等。
所有这些只是数字 n-1
在左侧填充了足够数量的零以使整个字符串长度为 10。
因此,要获得实际的第 n 个组合,您所要做的就是(在 Python 中的代码段下方,您可以轻松转换为 Java):
>>> def find_nth_combination(n):
... print "0" * (10-len(str(n-1))) + str(n-1)
...
>>> find_nth_combination(1)
0000000000
>>> find_nth_combination(100)
0000000099
>>> find_nth_combination(9062300000)
9062299999
>>> find_nth_combination(12300000)
0012299999
如果你想通过重复来解决这个问题,你可以have a look here(代码在Python)。
要获得所有排列,只需遍历所有数字。
因此,您需要执行以下操作:
for x in xrange(1, 1001):
find_nth_combination(x)
这将输出:
0000000000
0000000001
...
...
0000000997
0000000998
0000000999
没有重复:
因为你只需要第1000000000次排列,你可以暴力破解这个问题。从字符串开始:“0123456789”并使用 C++ 等效的 next_permuation,您可以在此处获取函数:http://codeforces.ru/blog/entry/3980。只要迭代一百万次,你就会得到解决方案。
有一个更高级的贪婪解决方案,运行速度非常快,您可以在这里阅读:https://math.stackexchange.com/questions/60742/finding-the-n-th-lexicographic-permutation-of-a-string
有重复:
这个问题很简单,有重复,因为它等同于只取数字并在开头用 0 填充它。
1st permuation: 000000001
15th permuation 000000015
etc.
我们需要先了解Factoradic Number System(或Factoradic number system)来解决这道题。阶乘数字系统使用阶乘值而不是数字的幂(二进制系统使用 2 的幂,十进制使用 10 的幂)来表示位值(或基数)。
位值(基数)为 –
5!= 120 4!= 24 3!=6 2!= 2 1!=1 0!=1 等等。 第零位的数字始终为 0。第一位的数字(基数 = 1!)可以是 0 或 1。第二位的数字(基数为 2!)可以是 0,1 或 2,依此类推在。一般来说,第n位的数字可以取0-n之间的任意值。
前几个数字表示为因数-
0 -> 0 = 0*0!
1 -> 10 = 1*1! + 0*0!
2 -> 100 = 1*2! + 0*1! + 0*0!
3 -> 110 = 1*2! + 1*1! + 0*0!
4 -> 200 = 2*2! + 0*1! + 0*0!
5 -> 210 = 2*2! + 1*1! + 0*0!
6 -> 1000 = 1*3! + 0*2! + 0*1! + 0*0!
7 -> 1010 = 1*3! + 0*2! + 1*1! + 0*0!
8 -> 1100 = 1*3! + 1*2! + 0*1! + 0*0!
9 -> 1110
10-> 1200
字符串的第 n 个字典顺序排列与其因子表示之间存在直接关系。
例如,这里是字符串“abcd”的排列。
0 abcd 6 bacd 12 cabd 18 dabc
1 abdc 7 badc 13 cadb 19 dacb
2 acbd 8 bcad 14 cbad 20 dbac
3 acdb 9 bcda 15 cbda 21 dbca
4 adbc 10 bdac 16 cdab 22 dcab
5 adcb 11 bdca 17 cdba 23 dcba
如果仔细观察,我们可以在这里看到一个规律。第一个字母在每 6 次(3!)次排列后发生变化。第二个字母在 2(2!) 次排列后发生变化。第三个字母在每次(1!)排列后改变,第四个字母在每次(0!)排列后改变。我们可以利用这个关系直接找到第n个排列。
一旦我们用因数表示法表示 n ,我们就会考虑其中的每个数字,并将给定字符串中的一个字符添加到输出中。如果我们需要找到“abcd”的第 14 个排列。 14 in factoradics -> 2100.
从第一个数字开始->2,字符串为‘abcd’。假设索引从 0 开始,从字符串中取出位置 2 的元素并将其添加到输出。
Output String
c abd
2 012
下一个数字 -> 1.String 现在是“abd”。再次,提取位置 1 的字符并将其添加到输出。
Output String
cb ad
21 01
下一个数字 -> 0。字符串是“ad”。将位置 1 的字符添加到输出。
Output String
cba d
210 0
下一个数字 -> 0。字符串是“d”。将位置 0 处的字符添加到输出。
Output String
cbad ''
2100
要将给定的数字转换为阶乘数字系统,请依次将数字除以 1、2、3、4、5 等等,直到商变为零。每一步的余数构成因式表示。
例如,要将 349 转换为因式,
Quotient Remainder Factorial Representation
349/1 349 0 0
349/2 174 1 10
174/3 58 0 010
58/4 14 2 2010
14/5 2 4 42010
2/6 0 2 242010
349 的因子表示是 242010。