将[N]下的所有幸运数字添加到向量的算法

Algorithm to add all lucky numbers under [N] to a vector?

本文关键字:幸运数字 添加 算法 向量      更新时间:2023-10-16

幸运数字被定义为一个正整数,其十进制表示形式仅包含幸运数字4和7。例如,数字47、744、4是幸运的,5、17、467不是。

现在,假设我想在不使用递归的情况下,将给定整数[N]下的所有幸运数字添加到向量中。为了简单起见,设N=1000。

我想出了一种方法,通过对1位数字、2位数字等进行单独的循环,只检查[N]下所有数字的每个数字。

for(int number=0;number<10;number++) {if(((number%10==4)||(number%10==7))) {lucky.push_back(number);}} //1 Digit Lucky Numbers
for(int number=10;number<100;number++) {if(((number%10==4)||(number%10==7))&&(((number/10)%10==7)||((number/10)%10==4))) {lucky.push_back(number);}} //2 Digit Lucky Numbers
for(int number=100;number<1000;number++) {if(((number%10==4)||(number%10==7))&&(((number/10)%10==7)||((number/10)%10==4))&&(((number/100)%10==7)||((number/100)%10==4))) {lucky.push_back(number);}} //3 Digit Lucky Numbers
for(int number=1000;number<10000;number++) {if(((number%10==4)||(number%10==7))&&(((number/10)%10==7)||((number/10)%10==4))&&(((number/100)%10==7)||((number/100)%10==4))&&(((number/1000)%10==7)||((number/1000)%10==4))) {lucky.push_back(number);}} //4 Digit Lucky Numbers

我在想,这可以大致转化为类似的东西,但我不太能想出确切的办法

for(number;number<10*itr_counter;number++)
    {
    if(((number%10*itr_counter==4)||(number%10*itr_counter==7))) {lucky.push_back(number);}
    itr_counter*=10;
    }

我基本上想通过取模10来检查所有1位数字中的每一位,并检查这些数字是4还是7。类似地,对于一个由X位组成的数字,我取模,将数字除以101100,依此类推,以检查4或7。

这是解决上述问题的好方法吗?另外,有人能帮助我将第一块代码优化为更小、更高效的代码吗?第二块代码中的某些内容会起作用。

// Use a D bit number as a proxy for a D digit number
// Then use L=2^D as a proxy for D in a loop through required values of D
// Notice D is only implied, we don't need to actually store it.
for (unsigned L=2; ; L*=2)
{
   // Loop through all D bit numbers (which happen to be all numbers less than L
   for (unsigned N=0; N<L; ++N)
   {
      // Convert that D bit number into a D digit number
      unsigned long long X = 0;
      // Loop through the bits of N converting to digits
      for ( unsigned B=L; (B>>=1)>0; )
      {
         X = X * 10 + 4;
         if ( B & N ) X += 3;  // change the 4 to a 7.
      }
      if ( X > MAX_NUMBER )
         return;  // break out of two levels of loop
      lucky.push_back( X );
   }
}

对于这样一项简单的任务来说,这可能显得过于棘手。但是,如果MAX_NUMBER非常大,这种方法比测试数字是否"幸运"要好得多。

还要注意,这些方法在代码流中的一个尴尬点(在两个级别的循环中)完成(检测完成)。在严肃的编程中,这种事情很常见。在这种情况下,我最喜欢的是将整个嵌套循环放入一个函数中,这样我就可以使用函数中的return作为break两级循环的简单方法。结构化编程狂热者可能会因为return的使用而受到极大的冒犯。通过将done标志混合到每个循环的控制中,可以实现相同的流控制。这种方法冒犯了我,就像return冒犯了结构化编程狂热者一样。

关于两个棘手循环的更多细节:

从概念上讲,我们有D,我们希望它从1开始向上计数,直到我们到达返回指令,我们希望计算L=1<<D,这意味着其唯一设置位在位置D的数字。但相反,我们完全跳过D,直接计算L。

然后,我们希望最内部的循环有一个比特数E,并在概念上向下计数D以下的比特位置:

for (E=D-1; E>=0; --E)

我们想类似地计算B=1<<E,作为唯一设置位在位置E的数字。但同样,我们没有D,也不需要E,我们可以直接计算B。

也许你可以制作一个散点图,并使用Gradient descendant算法和machine learning来估计你的值?

考虑到您不是在扫描列表以查找集合中的幸运数字,而是生成包含这些数字的0到N之间的所有可能数字组合的列表;使用一个简单的函数简单地生成所有幸运数字不是更有效吗?

由于您没有解析列表以验证数字是否为幸运数字,因此最好使用函数构建幸运数字的列表/向量。

解决这个问题的一种方法是用二进制数从0到指定的数字。对于n=1000,它将变为0b111 = 7,然后用4替换每个0,用7替换每个1。即CCD_ 12。然后,您可以很容易地对结果向量求和。

关于您将其验证为幸运数字的迭代方法,我会在pseduocode中创建如下函数:

while number > 0:
   lastDigit = number % 10
   if lastDigit != 4 and lastDigit != 7
       return false
   number = number / 10
return true
#include <iostream>
#include <vector>
bool is_lucky(int check_num)
{
while(check_num!=0)
    {
    if((check_num%10!=4)&&(check_num%10!=7))
        {
        return false;
        }
    check_num/=10;
    }
return true;
}
int main()
{
std::vector <long long> lucky;
for(int in_num=1;in_num<1000;in_num++)
    {
    if(is_lucky(in_num))
        {
        lucky.push_back(in_num);
        }
    }
}

我终于能够制作这个is_lucky函数了,它仍然不是完成任务的最有效方法,但似乎是完成任务最短的方法。

这个答案采用了Ottavio Campana的答案,但展示了良好软件工程的原则,即将工作的每个不同元素和设计决策的每个元素保留在代码中的一个位置(请参阅我对早期答案的评论):

// Anywhere a type is a design decision, rather than an obvious choice, use a typedef
typedef unsigned long long number;
typedef std::vector<number> container;
// A function is usually the best way to centralize work.
// If the parameter passing cost exceeds the work, use an inline function
// and rely on the optimizer to sort it out
inline bool push_number(container& c, number n, number m)
{
   if (n>m) return false;
   c.push_back(n);
   return true;
}

number n=0;
unsigned i=0;
container c;
while ( push_number(c,n+4,MAX_NUMBER) && push_number(c,n+7, MAX_NUMBER) )
{
   n = 10 * c[i++];
}

请注意,与最大值相比的>操作在代码中出现ONCE。4和7分别在代码中出现ONCE。10的乘积在代码中出现ONCE

当你遇到更复杂的问题时,组织这些事情的额外努力一度成为草率编程和真正的软件工程之间的最大区别。

如果MAX_NUMBER来自#define或任何全局位置,那么两次传递它比直接在函数中使用它意义小得多。但我反对#define做这样的事情,并几乎同样反对全局变量。所以我不想在最大值是全局可见的假设下进行编码。传球不好,但也不是很糟糕。在实际项目中,当您发现自己过度传递cMAX_NUMBER之类的东西时,这暗示着工作应该由类的成员函数来完成,该类将这些东西作为成员变量。

类似于埃拉托斯梯尼筛的解决方案有效,但它远非明智之举。

考虑将每个值放在有序向量中:第n个元素将由前面的一个元素加上最后的'4'o'7'组成。记住这一点,你可以生成数字,而不是检查它们。

的想法是

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#define MAX_NUMBER 1000
int main()
{
    std::vector<int> lucky_numbers(0);
    if (lucky_numbers.size () == 0)
    {
        if (4 < MAX_NUMBER)
            lucky_numbers.push_back (4);
        if (7 < MAX_NUMBER)
            lucky_numbers.push_back (7);
    }
    bool exit = false;
    while (!exit)
    {
        int size = lucky_numbers.size ();
        for (int i = 0 ; i < size ; i++)
        {
            int new_lucky_number = lucky_numbers[i] * 10 + 4;
            if (new_lucky_number < MAX_NUMBER &&
                std::find(lucky_numbers.begin(), lucky_numbers.end(), new_lucky_number) == lucky_numbers.end() )
            {
                lucky_numbers.push_back (new_lucky_number);
            }
            else if (new_lucky_number >= MAX_NUMBER)
            {
                exit = true;
                break;
            }
            new_lucky_number = lucky_numbers[i] * 10 + 7;
            if (new_lucky_number < MAX_NUMBER &&
                std::find(lucky_numbers.begin(), lucky_numbers.end(), new_lucky_number) == lucky_numbers.end() )
            {
                lucky_numbers.push_back (new_lucky_number);
            }
            else if (new_lucky_number >= MAX_NUMBER)
            {
                exit = true;
                break;
            }
        }
    }
    int size = lucky_numbers.size ();
    for (int i = 0 ; i < size ; i++)
        std::cout << lucky_numbers[i] << std::endl;
}

根据问题的具体情况,您可以优化std::find的使用,并使其更快。

更新

在讨论了解决方案之后,下面是增强版。检查仍然是内联的,但避免了查找重新启动。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#define MAX_NUMBER 1000
int main()
{
    std::vector<int> lucky_numbers(0);
    std::vector<int>::iterator it;
    if (lucky_numbers.size () == 0)
    {
        if (4 < MAX_NUMBER)
            lucky_numbers.push_back (4);
        if (7 < MAX_NUMBER)
            lucky_numbers.push_back (7);
    }
    for (int i = 0 ; ; i++)
    {
        int new_lucky_number = lucky_numbers[i] * 10 + 4;
        if (new_lucky_number < MAX_NUMBER)
            lucky_numbers.push_back (new_lucky_number);
        else
            break;
        new_lucky_number = lucky_numbers[i] * 10 + 7;
        if (new_lucky_number < MAX_NUMBER)
            lucky_numbers.push_back (new_lucky_number);
        else
            break;
    }
    for (int i = 0 ; i < lucky_numbers.size () ; i++)
        std::cout << lucky_numbers[i] << std::endl;
}