2012-01-29 107 views
8

我试图有效地列出1和100之间的数字。但是我必须摆脱具有相同数字的数字。
实施例: 12根据该规则是相同的21 13是31 14是41 所以for循环也不会越过相同的数字。
我在想一些技巧,比如从1到100的所有数字,然后删除找到的当前编号的排列。 我问这个的原因是因为像100000这样的大限制会失败。 又如: 124等于142,241,214,412,421C++中的唯一编号

+2

你为什么要得到相同的数字摆脱数字的?这是一个家庭作业问题吗? – 2012-01-29 20:52:14

+0

这里有问题吗? – talonmies 2012-01-29 20:53:09

+0

看起来像一个家庭作业。你有什么问题编写代码? – bratao 2012-01-29 20:54:49

回答

6

您可以申请递归。这个函数的原型是那么喜欢:

print_digits(int num_of_remaining_digits,int start_from_digit, int current_number); 

编辑:完成我在这里我的解决方案(我认为这不是从本福格特和升输出顺序

void print_digits(int num_of_remaining_digits,int start_from_digit, int current_number) 
{ 
    if(num_of_remaining_digits == 0) 
    { 
    std::cout << current_number << std::endl; 
    return; 
    } 

    for(int i=start_from_digit;i<=9;i++) 
    { 
    print_digits(num_of_remaining_digits-1,i,10*current_number+i); 
    } 
} 

更好readbility这里是测试代码

http://ideone.com/Xm8Mv

这是如何工作的?

它是递归中的经典之一。首先有停止条件。然后是主循环。
主循环从start_from_digit开始,因为所有生成的数字都将以非递减顺序排列。举例来说,如果current_number15它会调用print_digits蒙山

print_digits(num_of_remaining_digits-1,5,155) 
print_digits(num_of_remaining_digits-1,6,156) 
print_digits(num_of_remaining_digits-1,7,157) 
print_digits(num_of_remaining_digits-1,8,158) 
print_digits(num_of_remaining_digits-1,9,159) 

在每一个调用它会检查,如果我们达到最终白衣num_of_remaining_digits,如果不继续使用该被推为start_from_digit(第二PARAM)位数current_number

+0

不可思议!谢谢。正是我所需要的 – Ali 2012-01-29 21:47:26

+0

你能否详细解释一下我? – Ali 2012-01-29 21:49:24

+1

当问题被标记为“performance”时,最好远离'std :: cout'。否则一个很好的解 – 2012-01-29 21:51:00

0

好像它可以这样简单:

list = {} 
for (j = 1 to 100) 
    if (j is not excluded from list) 
      list += j; 

真的,只有if条件有趣的是:需要检查列表的所有相关属性项目。

1

我会通过超载正确的操作员(从我的头顶应该只是less)和std::set来写一个适合您比较需求的课程。

0

创建一个函数,该函数接受一个字符串,并返回一个字符串数组,其中包含该字符串中字符的所有可能排列组合。这并不难,但它可能是最简单的递归。虽然说起来容易做起来难。

一旦你有了这个函数,并且它返回数组,你只需要遍历数组并删除与数组中的一个共享数的indecies。

+0

感谢您的回答。我遇到的麻烦是,当我尝试应用这种方法时,上限大于等于5000万时,算法运行起来很慢。我试过在循环中不做任何事情,但增加了一个参数,但即使这样也会花费10秒以上。 – Ali 2012-01-29 20:57:48

+0

然后你有一个问题,因为这是测试每个可能的排列的唯一合理的方法。 – Zyerah 2012-01-30 01:36:25

1

我会用一个散列表,像这样

1)从派生的数字中派生出一个密钥,使得具有相同数字的数字具有相同的密钥(例如总和数字,所以“124”和“142”有关键7,或拿(+1)的乘积,因此“124”和“142”具有密钥30-必须对数字+1 +1)

2)将数字放入由密钥

索引的散列表中

现在,关于您是否已经拥有数字相同的数字的测试仅限于散列表中具有相同键的实体。这种算法需要线性存储,其性能取决于您可以提供多少密钥。

1

首先,观察你的规则与第一个数字= 1

生成所有2位数字不包括11(为什么?)

开始倍数现在,生成所有2位数第一个数字= 2,但不生成任何与第一个列表中的数字匹配的数字。

重复3,但不要从前两个列表中生成任何数字。

请注意,对于任何2位数的数字ab,如果符合条件,则必须是< b,否则您已经生成了相应的数字ba。

在PASCAL语言中,只是因为我觉得一文不值:

var i:integer; j:integer; 
begin 
    for i := 1 to 8 do 
    for j := i+1 to 9 do 
     println(i*10+j); 
end; 

增加了稍晚

注意要生成的数字总是有自己的数字严格单调递增。对于数字2abc资格,观察2 < a < b < c。 (实施例:2539是一个匹配2359,并应被拒绝。)

+0

相同的数字a Ali 2012-01-29 21:10:01

0

我会使用一个set为的号码的数字的置换:

std::vector<int> list_unwanted = digit_permutations(number); 
std::unordered_set<int> set_unwanted(begin(list_unwanted), end(list_unwanted)); 

然后循环从0到极限,通过检查增加不必要的数字,如果他们在集合set_unwanted

std::vector<int> numbers; 
numbers.reserve(limit - set_unwanted.count()); 

for (int i = 0; i < limit; ++i) 
    if (!set_unwanted.count(i)) 
0

如果你有一组数字,这组的任何排列是不是一个有效的解决方案,所以首先进行功能如果一组数字是另一组的排列,则确定R集。 要获得单个数字,您可以递归地除以10,直到获得零值。 如果你把所有的数字像[1,2,4]数组,以检查是否antoher阵列是antoher集的置换(你选择了它,只有当他们具有相同的长度):

bool permutation(int *a, int *b, int n) // n leading dimension 
{ 
    bool result=true, vector[n]={false}; 
    for(int i=0;i<n;i++) 
    { 
     for(int j=0;j<n ;j++) 
     { 
      if(a[i]==b[j]) 
       vector[i]=false; 
     } 
    } 
    for(int i=0;i<n && result;i++) 
     result=(vector[i]==true); // if vector[i] is false, result is false, is 
            // not a permutation and the loop ends 
    return result; 
} 

我没有测试过,但我认为它有效,否则告诉我。 至于把所有的数字放在一个数组中,我认为这很容易。 一旦生成所有数字,您检查确定的数字不是一个已经采取的数字的排列。

+0

如果我不明白错误,它适用,就好像一个数字是预填的数字的排列组合。谢谢你的贡献,但我更喜欢不**不寻找未来的数字。如果发现号码124发现循环**不应该再次超过241,214。 – Ali 2012-01-29 21:17:42

+0

我意识到这是错误的,如果我有一个数组[1,1,1]和[1,2,3],这个函数返回true。 – 2012-01-29 21:23:39

1
#include <stdio.h> 

size_t enum_canonical(char* begin, char* end, char min, char max) 
{ 
    if (begin == end) { 
     puts(begin); 
     putchar('\n'); 
     return 1; 
    } 

    size_t result_count = 0; 
    --end; 
    for(*end = min; *end <= max; ++*end) 
     result_count += enum_canonical(begin, end, min, *end); 

    return result_count; 
} 

int main(void) 
{ 
    char buff[7]; 
    printf("%d results\n", enum_canonical(buff, &(buff[6] = '\0'), '0', '9')); 
} 

演示:http://ideone.com/BWGdg

+0

非常感谢您的帮助。 – Ali 2012-01-29 21:23:02

+0

@rolandbishop:请注意,这与[ralu]提到的方法(http://stackoverflow.com/a/9056758/103167)完全相同。 – 2012-01-29 21:23:58

+0

+1,但仍然这个代码将运行得像你的一样快http://ideone.com/RTXqQ :) – 2012-01-29 23:06:47

0

这是我的想法,每个值把它的数字在一组。使用该设置作为另一组的密钥,以跟踪哪些数字已被使用。在我的情况下,我使用位字段作为数字的集合,即数字0用1表示,数字1用2表示(2等于4等等)。太累了解释,这里的塔codez:

unsigned int get_digits(int v) 
{ 
    unsigned int rv = 0; 
    do 
    { 
    rv |= 1 << (v % 10); 
    v /= 10; 
    } while(v); 
    return rv; 
} 

void unique_ints(int n) 
{ 
    std::set<unsigned int> used_combinations; 
    for(int i = 0; i < n; ++i) 
    { 
    const unsigned int d = get_digits(i); 
    if(used_combinations.find(d) == used_combinations.end()) 
    { 
     used_combinations.insert(d); 
     // cout or some other way to store the value 
     std::cout << i << std::endl; 
    } 
    } 
} 
1

让我们1到1000由于有4位在1000,我打印1为0001,所以0001,0010,0100,1000是相同数量按我算法。 0120,0012,0210,0102,0201,0021也是相同的数字。

下面是程序:

int main() 
{ 
    int i=0, j=0, k=0; 

    while(i<=9) 
    { 
     int unique=(i*100 + j*10 + k); 
     printf("unique number : %3d\n", unique); 

     if(j==9 && k==9) 
     { 
      i++; 
      k=i; 
      j=i; 
     } 
     else if(k==9) 
     { 
      j++; 
      k=j; 
     } 
     else 
      k++; 
    } 

}