简体   繁体   English

以递增的顺序打印形式2 ^ i * 5 ^ j的数字

[英]Printing numbers of the form 2^i * 5^j in increasing order

How do you print numbers of form 2^i * 5^j in increasing order. 如何以递增的顺序打印表格2^i * 5^j数字。

For eg:
1, 2, 4, 5, 8, 10, 16, 20

This is actually a very interesting question, especially if you don't want this to be N^2 or NlogN complexity. 这实际上是一个非常有趣的问题,特别是如果你不希望这是N ^ 2或NlogN的复杂性。

What I would do is the following: 我要做的是以下内容:

  • Define a data structure containing 2 values (i and j) and the result of the formula. 定义包含2个值(i和j)的数据结构以及公式的结果。
  • Define a collection (eg std::vector) containing this data structures 定义包含此数据结构的集合(例如std :: vector)
  • Initialize the collection with the value (0,0) (the result is 1 in this case) 使用值(0,0)初始化集合(在这种情况下结果为1)
  • Now in a loop do the following: 现在循环执行以下操作:
    • Look in the collection and take the instance with the smallest value 查看集合并获取具有最小值的实例
    • Remove it from the collection 将其从集合中删除
    • Print this out 打印出来
    • Create 2 new instances based on the instance you just processed 根据刚处理的实例创建2个新实例
      • In the first instance increment i 在第一个实例中增量i
      • In the second instance increment j 在第二个实例中增加j
    • Add both instances to the collection (if they aren't in the collection yet) 将两个实例添加到集合中(如果它们尚未在集合中)
  • Loop until you had enough of it 循环,直到你有足够的

The performance can be easily tweaked by choosing the right data structure and collection. 通过选择正确的数据结构和集合,可以轻松调整性能。 Eg in C++, you could use an std::map, where the key is the result of the formula, and the value is the pair (i,j). 例如,在C ++中,您可以使用std :: map,其中键是公式的结果,值是对(i,j)。 Taking the smallest value is then just taking the first instance in the map (*map.begin()). 取最小值然后只取地图中的第一个实例(* map.begin())。

I quickly wrote the following application to illustrate it (it works!, but contains no further comments, sorry): 我快速编写了以下应用程序来说明它(它有效!但不包含其他评论,抱歉):

#include <math.h>
#include <map>
#include <iostream>

typedef __int64 Integer;

typedef std::pair<Integer,Integer> MyPair;
typedef std::map<Integer,MyPair> MyMap;

Integer result(const MyPair &myPair)
{
return pow((double)2,(double)myPair.first) * pow((double)5,(double)myPair.second);
}

int main()
{
MyMap myMap;
MyPair firstValue(0,0);

myMap[result(firstValue)] = firstValue;

while (true)
   {
   auto it=myMap.begin();
   if (it->first < 0) break;        // overflow

   MyPair myPair = it->second;
   std::cout << it->first << "= 2^" << myPair.first << "*5^" << myPair.second << std::endl;

   myMap.erase(it);

   MyPair pair1 = myPair;
   ++pair1.first;
   myMap[result(pair1)] = pair1;

   MyPair pair2 = myPair;
   ++pair2.second;
   myMap[result(pair2)] = pair2;
   }
}

This is well suited to a functional programming style. 这非常适合函数式编程风格。 In F#: 在F#中:

let min (a,b)= if(a<b)then a else b;;
type stream (current, next)=
    member this.current = current
    member this.next():stream = next();;
let rec merge(a:stream,b:stream)=
    if(a.current<b.current) then new stream(a.current, fun()->merge(a.next(),b))
    else new stream(b.current, fun()->merge(a,b.next()));;

let rec Squares(start) = new stream(start,fun()->Squares(start*2));;

let rec AllPowers(start) = new stream(start,fun()->merge(Squares(start*2),AllPowers(start*5)));;
let Results = AllPowers(1);;

Works well with Results then being a stream type with current value and a next method. 适用于结果,然后是具有当前值和下一个方法的流类型。

Walking through it: 走过它:

  1. I define min for completenes. 我为minnes定义了min。
  2. I define a stream type to have a current value and a method to return a new string, essentially head and tail of a stream of numbers. 我定义了一个流类型,它具有一个当前值和一个返回一个新字符串的方法,基本上是数字流的头部和尾部。
  3. I define the function merge, which takes the smaller of the current values of two streams and then increments that stream. 我定义了函数merge,它接受两个流的当前值中较小的一个,然后递增该流。 It then recurses to provide the rest of the stream. 然后递归以提供流的其余部分。 Essentially, given two streams which are in order, it will produce a new stream which is in order. 基本上,给定两个按顺序排列的流,它将生成一个按顺序排列的新流。
  4. I define squares to be a stream increasing in powers of 2. 我将正方形定义为以2的幂增加的流。
  5. AllPowers takes the start value and merges the stream resulting from all squares at this number of powers of 5. it with the stream resulting from multiplying it by 5, since these are your only two options. AllPowers获取起始值,并将所有方块产生的流合并为5的这个数量,并将其乘以5得到的流,因为这是您唯一的两个选项。 You effectively are left with a tree of results 你实际上留下了一棵结果树

The result is merging more and more streams, so you merge the following streams 结果是合并越来越多的流,因此您合并以下流

1, 2, 4, 8, 16, 32... 1,2,4,8,16,32 ......

5, 10, 20, 40, 80, 160... 5,10,20,40,80,160 ......

25, 50, 100, 200, 400... 25,50,100,200,400 ......

.

.

. Merging all of these turns out to be fairly efficient with tail recursio and compiler optimisations etc. 通过尾递归和编译器优化等来合并所有这些变得相当有效。

These could be printed to the console like this: 这些可以像这样打印到控制台:

let rec PrintAll(s:stream)=
    if (s.current > 0) then
        do System.Console.WriteLine(s.current)
        PrintAll(s.next());;

PrintAll(Results);

let v = System.Console.ReadLine();

Similar things could be done in any language which allows for recursion and passing functions as values (it's only a little more complex if you can't pass functions as variables). 类似的事情可以在任何语言中完成,它允许递归并将函数作为值传递(如果你不能将函数作为变量传递,它只会稍微复杂一些)。

For an O(N) solution, you can use a list of numbers found so far and two indexes: one representing the next number to be multiplied by 2, and the other the next number to be multiplied by 5. Then in each iteration you have two candidate values to choose the smaller one from. 对于O(N)解决方案,您可以使用到目前为止找到的数字列表和两个索引:一个代表下一个要乘以2的数字,另一个代表下一个要乘以5的数字。然后在每次迭代中有两个候选值来选择较小的一个。

In Python: 在Python中:

 numbers = [1]
 next_2 = 0
 next_5 = 0

 for i in xrange(100):
     mult_2 = numbers[next_2]*2
     mult_5 = numbers[next_5]*5

     if mult_2 < mult_5:
        next = mult_2
        next_2 += 1
     else:
        next = mult_5
        next_5 += 1

     # The comparison here is to avoid appending duplicates
     if next > numbers[-1]:
        numbers.append(next)

 print numbers

So we have two loops, one incrementing i and second one incrementing j starting both from zero, right? 所以我们有两个循环,一个递增i ,第二个递增j从零开始,对吧? (multiply symbol is confusing in the title of the question) (乘以符号在问题的标题中令人困惑)

You can do something very straightforward: 你可以做一些非常简单的事情:

  1. Add all items in an array 添加数组中的所有项目
  2. Sort the array 对数组进行排序

Or you need an other solution with more math analysys? 或者你需要一个更多数学分析的其他解决方案?

EDIT: More smart solution by leveraging similarity with Merge Sort problem 编辑:利用Merge Sort问题的相似性,提供更智能的解决方案

If we imagine infinite set of numbers of 2^i and 5^j as two independent streams/lists this problem looks very the same as well known Merge Sort problem. 如果我们将2^i5^j无限数组想象为两个独立的流/列表,则此问题看起来与众所周知的合并排序问题非常相似。

So solution steps are: 所以解决方案步骤是:

  • Get two numbers one from the each of streams (of 2 and of 5) 从每个流中获取两个数字(2个和5个)
  • Compare 相比
  • Return smallest 回报最小
  • get next number from the stream of the previously returned smallest 从先前返回的最小值的流中获取下一个数字

and that's it! 就是这样! ;) ;)

PS: Complexity of Merge Sort always is O(n*log(n)) PS:合并的复杂性排序always O(n*log(n))

I visualize this problem as a matrix M where M(i,j) = 2^i * 5^j . 我将该问题可视化为矩阵M ,其中M(i,j) = 2^i * 5^j This means that both the rows and columns are increasing. 这意味着行和列都在增加。

Think about drawing a line through the entries in increasing order, clearly beginning at entry (1,1) . 考虑按递增顺序在条目中画一条线,显然从条目(1,1) As you visit entries, the row and column increasing conditions ensure that the shape formed by those cells will always be an integer partition (in English notation). 当您访问条目时,行和列增加条件可确保由这些单元格形成的形状始终为整数分区 (英文表示法)。 Keep track of this partition ( mu = (m1, m2, m3, ...) where mi is the number of smaller entries in row i -- hence m1 >= m2 >= ... ). 跟踪这个分区( mu = (m1, m2, m3, ...) ,其中mi是第i行中较小条目的数量 - 因此m1 >= m2 >= ... )。 Then the only entries that you need to compare are those entries which can be added to the partition. 然后,您需要比较的唯一条目是可以添加到分区的条目。

Here's a crude example. 这是一个粗略的例子。 Suppose you've visited all the x s ( mu = (5,3,3,1) ), then you need only check the @ s: 假设你已经访问了所有x s( mu = (5,3,3,1) ),那么你只需要检查@ s:

x x x x x @
x x x @
x x x 
x @
@

Therefore the number of checks is the number of addable cells (equivalently the number of ways to go up in Bruhat order if you're of a mind to think in terms of posets). 因此,检查的数量是可添加单元格的数量(相当于如果你想要以posets方式思考的话,以Bruhat顺序上升的方式的数量)。

Given a partition mu , it's easy to determine what the addable states are. 给定分区mu ,很容易确定可添加状态是什么。 Image an infinite string of 0 s following the last positive entry. 在最后一个正面条目之后成像0秒的无限字符串。 Then you can increase mi by 1 if and only if m(i-1) > mi . 那么当且仅当m(i-1) > mi你可以将mi增加1

Back to the example, for mu = (5,3,3,1) we can increase m1 (6,3,3,1) or m2 (5,4,3,1) or m4 (5,3,3,2) or m5 (5,3,3,1,1) . 回到例子,对于mu = (5,3,3,1)我们可以增加m1 (6,3,3,1)m2 (5,4,3,1)m4 (5,3,3,2)m5 (5,3,3,1,1)

The solution to the problem then finds the correct sequence of partitions (saturated chain). 然后,问题的解决方案找到正确的分区序列(饱和链)。 In pseudocode: 在伪代码中:

mu = [1,0,0,...,0];
while (/* some terminate condition or go on forever */) {
    minNext = 0;
    nextCell = [];
    // look through all addable cells
    for (int i=0; i<mu.length; ++i) {
        if (i==0 or mu[i-1]>mu[i]) {
            // check for new minimum value
            if (minNext == 0 or 2^i * 5^(mu[i]+1) < minNext) {
                nextCell = i;
                minNext = 2^i * 5^(mu[i]+1)
            }
        }
    }
    // print next largest entry and update mu
    print(minNext);
    mu[i]++;
}

I wrote this in Maple stopping after 12 iterations: 我在Maple中写了这个,经过12次迭代后停止:

1, 2, 4, 5, 8, 10, 16, 20, 25, 32, 40, 50

and the outputted sequence of cells added and got this: 并输出了输出的细胞序列,得到了这个:

1  2  3  5  7 10
4  6  8  11 
9  12

corresponding to this matrix representation: 对应于此矩阵表示:

1, 2, 4, 8, 16, 32...

5, 10, 20, 40, 80, 160...

25, 50, 100, 200, 400...

I'm sure everyone one's might have got the answer by now, but just wanted to give a direction to this solution.. 我相信现在每个人都可能得到了答案,但只是想给这个解决方案指明方向..

It's a Ctrl C + Ctrl V from http://www.careercup.com/question?id=16378662 这是来自http://www.careercup.com/question?id=16378662的Ctrl C + Ctrl V.

 void print(int N)
  {
     int arr[N];
     arr[0] = 1;
     int i = 0, j = 0, k = 1;
     int numJ, numI;
     int num;
       for(int count = 1; count < N; )
        {
          numI = arr[i] * 2;
          numJ = arr[j] * 5;

            if(numI < numJ)
             {
               num = numI;
               i++;
             }

           else
            {
              num = numJ;
              j++;
            }

            if(num > arr[k-1])
            {
             arr[k] = num;
             k++;
             count++;
            }

       }

     for(int counter = 0; counter < N; counter++)
     {
      printf("%d ", arr[counter]);
     }
}

The question as put to me was to return an infinite set of solutions. 提出的问题是返回一组无限的解决方案。 I pondered the use of trees, but felt there was a problem with figuring out when to harvest and prune the tree, given an infinite number of values for i & j. 我考虑使用树木,但觉得在确定何时收获和修剪树木时存在问题,因为i&j具有无限数量的值。 I realized that a sieve algorithm could be used. 我意识到可以使用筛分算法。 Starting from zero, determine whether each positive integer had values for i and j. 从零开始,确定每个正整数是否具有i和j的值。 This was facilitated by turning answer = (2^i)*(2^j) around and solving for i instead. 通过转动answer =(2 ^ i)*(2 ^ j)并解决i来促进这一点。 That gave me i = log2 (answer/ (5^j)). 这给了我i = log2(答案/(5 ^ j))。 Here is the code: 这是代码:

class Program
{
static void Main(string[] args)
{
    var startTime = DateTime.Now;

    int potential = 0;

    do
    {
        if (ExistsIandJ(potential))
            Console.WriteLine("{0}", potential);
            potential++;
    } while (potential < 100000);

    Console.WriteLine("Took {0} seconds", DateTime.Now.Subtract(startTime).TotalSeconds);

}

private static bool ExistsIandJ(int potential)
{
    // potential = (2^i)*(5^j)
    // 1 = (2^i)*(5^j)/potential
    // 1/(2^1) = (5^j)/potential or (2^i) = potential / (5^j)
    // i = log2 (potential / (5^j))

    for (var j = 0; Math.Pow(5,j) <= potential; j++)
    {
        var i = Math.Log(potential / Math.Pow(5, j), 2);
        if (i == Math.Truncate(i))
            return true;
    }
    return false;
}
}

First of all, (as others mentioned already) this question is very vague!!! 首先,(正如其他人已经提到的)这个问题很模糊!

Nevertheless, I am going to give a shot based on your vague equation and the pattern as your expected result. 不过,我打算根据你的模糊方程和模式作为预期结果。 So I am not sure the following will be true for what you are trying to do, however it may give you some idea about java collections! 因此,我不确定以下内容是否适用于您正在尝试的内容,但是它可能会让您对Java集合有所了解!

import java.util.List;
import java.util.ArrayList;
import java.util.SortedSet;
import java.util.TreeSet;


public class IncreasingNumbers {

    private static List<Integer> findIncreasingNumbers(int maxIteration) {
        SortedSet<Integer> numbers = new TreeSet<Integer>();
        SortedSet<Integer> numbers2 = new TreeSet<Integer>();

        for (int i=0;i < maxIteration;i++) {
            int n1 = (int)Math.pow(2, i);
            numbers.add(n1);

            for (int j=0;j < maxIteration;j++) {
                int n2 = (int)Math.pow(5, i);
                numbers.add(n2);

                for (Integer n: numbers) {
                    int n3 = n*n1;
                    numbers2.add(n3);
                }
            }
        }

        numbers.addAll(numbers2);

        return new ArrayList<Integer>(numbers);
    }

    /**
     * Based on the following fuzzy question @ StackOverflow
     * http://stackoverflow.com/questions/7571934/printing-numbers-of-the-form-2i-5j-in-increasing-order
     * 
     * 
     * Result:
     * 1 2 4 5 8 10 16 20 25 32 40 64 80 100 125 128 200 256 400 625 1000 2000 10000 
     */
    public static void main(String[] args) {
        List<Integer> numbers = findIncreasingNumbers(5);

        for (Integer i: numbers) {
            System.out.print(i + " ");
        }
    }
}

If you can do it in O(nlogn), here's a simple solution: 如果你能在O(nlogn)中做到这一点,这里有一个简单的解决方案:

Get an empty min-heap
Put 1 in the heap
while (you want to continue)
    Get num from heap
    print num
    put num*2 and num*5 in the heap

There you have it. 你有它。 By min-heap, I mean min-heap 通过min-heap,我的意思是min-heap

As a mathematician the first thing I always think about when looking at something like this is "will logarithms help?". 作为一名数学家,在看这样的事情时,我总是会想到的第一件事是“会对数有帮助吗?”。

In this case it might. 在这种情况下它可能。

If our series A is increasing then the series log(A) is also increasing. 如果我们的系列A增加,则系列日志(A)也在增加。 Since all terms of A are of the form 2^i.5^j then all members of the series log(A) are of the form i.log(2) + j.log(5) 由于A的所有项都是2 ^ i.5 ^ j的形式,因此系列log(A)的所有成员的形式为i.log(2)+ j.log(5)

We can then look at the series log(A)/log(2) which is also increasing and its elements are of the form i+j.(log(5)/log(2)) 然后我们可以查看系列log(A)/ log(2),它也在增加,其元素的形式为i + j。(log(5)/ log(2))

If we work out the i and j that generates the full ordered list for this last series (call it B) then that i and j will also generate the series A correctly. 如果我们计算出生成最后一个系列的完整有序列表的i和j(称之为B),那么i和j也将正确生成A系列。

This is just changing the nature of the problem but hopefully to one where it becomes easier to solve. 这只是改变了问题的本质,但希望能够更容易解决。 At each step you can either increase i and decrease j or vice versa. 在每一步,您可以增加i并减少j,反之亦然。

Looking at a few of the early changes you can make (which I will possibly refer to as transforms of i,j or just transorms) gives us some clues of where we are going. 看看你可以做出的一些早期变化(我可能会将其称为i,j的变换或只是变换)为我们提供了一些我们前进的线索。

Clearly increasing i by 1 will increase B by 1. However, given that log(5)/log(2) is approx 2.3 then increasing j by 1 while decreasing i by 2 will given an increase of just 0.3 . 显然将i增加1将使B增加1.然而,假设log(5)/ log(2)约为2.3然后将j增加1而将i减小2将增加仅0.3。 The problem then is at each stage finding the minimum possible increase in B for changes of i and j. 那么问题是在每个阶段找到B对于i和j的变化的最小可能增加。

To do this I just kept a record as I increased of the most efficient transforms of i and j (ie what to add and subtract from each) to get the smallest possible increase in the series. 为了做到这一点,我只保留了一个记录,因为我增加了i和j的最有效变换(即从每个变换中添加和减去的变换)以获得系列中可能的最小增量。 Then applied whichever one was valid (ie making sure i and j don't go negative). 然后应用任何一个有效的(即确保i和j不变为负数)。

Since at each stage you can either decrease i or decrease j there are effectively two classes of transforms that can be checked individually. 因为在每个阶段你可以减少i或减少j,实际上有两类变换可以单独检查。 A new transform doesn't have to have the best overall score to be included in our future checks, just better than any other in its class. 新的转换不一定要包含在我们未来的检查中,只比其他同类产品更好。

To test my thougths I wrote a sort of program in LinqPad. 为了测试我的思想,我在LinqPad中编写了一个程序。 Key things to note are that the Dump() method just outputs the object to screen and that the syntax/structure isn't valid for a real c# file. 需要注意的一点是,Dump()方法只是将对象输出到屏幕,并且语法/结构对于真正的c#文件无效。 Converting it if you want to run it should be easy though. 如果你想运行它,转换它应该很容易。

Hopefully anything not explicitly explained will be understandable from the code. 希望任何未明确解释的内容都可以从代码中理解。

void Main()
{
    double C = Math.Log(5)/Math.Log(2);
    int i = 0;
    int j = 0;
    int maxi = i;
    int maxj = j;

    List<int> outputList = new List<int>();
    List<Transform> transforms = new List<Transform>();
    outputList.Add(1);
    while (outputList.Count<500)
    {
    Transform tr;
        if (i==maxi)
        {
            //We haven't considered i this big before. Lets see if we can find an efficient transform by getting this many i and taking away some j.
            maxi++;
            tr = new Transform(maxi, (int)(-(maxi-maxi%C)/C), maxi%C);
            AddIfWorthwhile(transforms, tr);
        }
        if (j==maxj)
        {
            //We haven't considered j this big before. Lets see if we can find an efficient transform by getting this many j and taking away some i.
            maxj++;
            tr = new Transform((int)(-(maxj*C)), maxj, (maxj*C)%1);
            AddIfWorthwhile(transforms, tr);
        }
        //We have a set of transforms. We first find ones that are valid then order them by score and take the first (smallest) one.
        Transform bestTransform = transforms.Where(x=>x.I>=-i && x.J >=-j).OrderBy(x=>x.Score).First();
        //Apply transform
        i+=bestTransform.I;
        j+=bestTransform.J;
        //output the next number in out list.
        int value = GetValue(i,j);
        //This line just gets it to stop when it overflows. I would have expected an exception but maybe LinqPad does magic with them?
        if (value<0) break;
        outputList.Add(value);
    }
    outputList.Dump();

}

public int GetValue(int i, int j)
{
    return (int)(Math.Pow(2,i)*Math.Pow(5,j));
}

public void AddIfWorthwhile(List<Transform> list, Transform tr)
{
    if (list.Where(x=>(x.Score<tr.Score && x.IncreaseI == tr.IncreaseI)).Count()==0)
    {
        list.Add(tr);
    }
}

// Define other methods and classes here
    public class Transform
    {
        public int I;
        public int J;
        public double Score;
        public bool IncreaseI
        {
            get {return I>0;}
        }

        public Transform(int i, int j, double score)
        {
            I=i;
            J=j;
            Score=score;
        }
    }

I've not bothered looking at the efficiency of this but I strongly suspect its better than some other solutions because at each stage all I need to do is check my set of transforms - working out how many of these there are compared to "n" is non-trivial. 我没有费心去看这个效率,但我强烈怀疑它比其他一些解决方案更好,因为在每个阶段,我需要做的就是检查我的变换集 - 计算出与“n”相比有多少变换是非平凡的。 It is clearly related since the further you go the more transforms there are but the number of new transforms becomes vanishingly small at higher numbers so maybe its just O(1). 它显然是相关的,因为你越往前就会有更多的变换,但是新变换的数量在更高的数字时变得越来越小,所以也许它只是O(1)。 This O stuff always confused me though. 这个O东西总是让我困惑。 ;-) ;-)

One advantage over other solutions is that it allows you to calculate i,j without needing to calculate the product allowing me to work out what the sequence would be without needing to calculate the actual number itself. 与其他解决方案相比,一个优点是它允许您计算i,j而无需计算产品,从而可以计算出序列的内容,而无需计算实际数字本身。

For what its worth after the first 230 nunmbers (when int runs out of space) I had 9 transforms to check each time. 为了它在前230个nunmbers之后的价值(当int用完空间时)我每次都要进行9次变换检查。 And given its only my total that overflowed I ran if for the first million results and got to i=5191 and j=354. 并且考虑到它只有我的总数溢出,我跑了,如果为第一百万个结果,并得到i = 5191和j = 354。 The number of transforms was 23. The size of this number in the list is approximately 10^1810. 变换的数量是23.该列表中该数字的大小约为10 ^ 1810。 Runtime to get to this level was approx 5 seconds. 达到这个水平的运行时间约为5秒。

PS If you like this answer please feel free to tell your friends since I spent ages on this and a few +1s would be nice compensation. PS如果你喜欢这个答案,请随时告诉你的朋友,因为我花了很多年的时间和几个+ 1s将是很好的补偿。 Or in fact just comment to tell me what you think. 或者实际上只是评论告诉我你的想法。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM