簡體   English   中英

以遞增的順序打印形式2 ^ i * 5 ^ j的數字

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

如何以遞增的順序打印表格2^i * 5^j數字。

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

這實際上是一個非常有趣的問題,特別是如果你不希望這是N ^ 2或NlogN的復雜性。

我要做的是以下內容:

  • 定義包含2個值(i和j)的數據結構以及公式的結果。
  • 定義包含此數據結構的集合(例如std :: vector)
  • 使用值(0,0)初始化集合(在這種情況下結果為1)
  • 現在循環執行以下操作:
    • 查看集合並獲取具有最小值的實例
    • 將其從集合中刪除
    • 打印出來
    • 根據剛處理的實例創建2個新實例
      • 在第一個實例中增量i
      • 在第二個實例中增加j
    • 將兩個實例添加到集合中(如果它們尚未在集合中)
  • 循環,直到你有足夠的

通過選擇正確的數據結構和集合,可以輕松調整性能。 例如,在C ++中,您可以使用std :: map,其中鍵是公式的結果,值是對(i,j)。 取最小值然后只取地圖中的第一個實例(* map.begin())。

我快速編寫了以下應用程序來說明它(它有效!但不包含其他評論,抱歉):

#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;
   }
}

這非常適合函數式編程風格。 在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);;

適用於結果,然后是具有當前值和下一個方法的流類型。

走過它:

  1. 我為minnes定義了min。
  2. 我定義了一個流類型,它具有一個當前值和一個返回一個新字符串的方法,基本上是數字流的頭部和尾部。
  3. 我定義了函數merge,它接受兩個流的當前值中較小的一個,然后遞增該流。 然后遞歸以提供流的其余部分。 基本上,給定兩個按順序排列的流,它將生成一個按順序排列的新流。
  4. 我將正方形定義為以2的冪增加的流。
  5. AllPowers獲取起始值,並將所有方塊產生的流合並為5的這個數量,並將其乘以5得到的流,因為這是您唯一的兩個選項。 你實際上留下了一棵結果樹

結果是合並越來越多的流,因此您合並以下流

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

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

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

通過尾遞歸和編譯器優化等來合並所有這些變得相當有效。

這些可以像這樣打印到控制台:

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();

類似的事情可以在任何語言中完成,它允許遞歸並將函數作為值傳遞(如果你不能將函數作為變量傳遞,它只會稍微復雜一些)。

對於O(N)解決方案,您可以使用到目前為止找到的數字列表和兩個索引:一個代表下一個要乘以2的數字,另一個代表下一個要乘以5的數字。然后在每次迭代中有兩個候選值來選擇較小的一個。

在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

所以我們有兩個循環,一個遞增i ,第二個遞增j從零開始,對吧? (乘以符號在問題的標題中令人困惑)

你可以做一些非常簡單的事情:

  1. 添加數組中的所有項目
  2. 對數組進行排序

或者你需要一個更多數學分析的其他解決方案?

編輯:利用Merge Sort問題的相似性,提供更智能的解決方案

如果我們將2^i5^j無限數組想象為兩個獨立的流/列表,則此問題看起來與眾所周知的合並排序問題非常相似。

所以解決方案步驟是:

  • 從每個流中獲取兩個數字(2個和5個)
  • 相比
  • 回報最小
  • 從先前返回的最小值的流中獲取下一個數字

就是這樣! ;)

PS:合並的復雜性排序always O(n*log(n))

我將該問題可視化為矩陣M ,其中M(i,j) = 2^i * 5^j 這意味着行和列都在增加。

考慮按遞增順序在條目中畫一條線,顯然從條目(1,1) 當您訪問條目時,行和列增加條件可確保由這些單元格形成的形狀始終為整數分區 (英文表示法)。 跟蹤這個分區( mu = (m1, m2, m3, ...) ,其中mi是第i行中較小條目的數量 - 因此m1 >= m2 >= ... )。 然后,您需要比較的唯一條目是可以添加到分區的條目。

這是一個粗略的例子。 假設你已經訪問了所有x s( mu = (5,3,3,1) ),那么你只需要檢查@ s:

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

因此,檢查的數量是可添加單元格的數量(相當於如果你想要以posets方式思考的話,以Bruhat順序上升的方式的數量)。

給定分區mu ,很容易確定可添加狀態是什么。 在最后一個正面條目之后成像0秒的無限字符串。 那么當且僅當m(i-1) > mi你可以將mi增加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)

然后,問題的解決方案找到正確的分區序列(飽和鏈)。 在偽代碼中:

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]++;
}

我在Maple中寫了這個,經過12次迭代后停止:

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

並輸出了輸出的細胞序列,得到了這個:

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

對應於此矩陣表示:

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

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

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

我相信現在每個人都可能得到了答案,但只是想給這個解決方案指明方向..

這是來自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]);
     }
}

提出的問題是返回一組無限的解決方案。 我考慮使用樹木,但覺得在確定何時收獲和修剪樹木時存在問題,因為i&j具有無限數量的值。 我意識到可以使用篩分算法。 從零開始,確定每個正整數是否具有i和j的值。 通過轉動answer =(2 ^ i)*(2 ^ j)並解決i來促進這一點。 這給了我i = log2(答案/(5 ^ j))。 這是代碼:

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;
}
}

首先,(正如其他人已經提到的)這個問題很模糊!

不過,我打算根據你的模糊方程和模式作為預期結果。 因此,我不確定以下內容是否適用於您正在嘗試的內容,但是它可能會讓您對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 + " ");
        }
    }
}

如果你能在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

你有它。 通過min-heap,我的意思是min-heap

作為一名數學家,在看這樣的事情時,我總是會想到的第一件事是“會對數有幫助嗎?”。

在這種情況下它可能。

如果我們的系列A增加,則系列日志(A)也在增加。 由於A的所有項都是2 ^ i.5 ^ j的形式,因此系列log(A)的所有成員的形式為i.log(2)+ j.log(5)

然后我們可以查看系列log(A)/ log(2),它也在增加,其元素的形式為i + j。(log(5)/ log(2))

如果我們計算出生成最后一個系列的完整有序列表的i和j(稱之為B),那么i和j也將正確生成A系列。

這只是改變了問題的本質,但希望能夠更容易解決。 在每一步,您可以增加i並減少j,反之亦然。

看看你可以做出的一些早期變化(我可能會將其稱為i,j的變換或只是變換)為我們提供了一些我們前進的線索。

顯然將i增加1將使B增加1.然而,假設log(5)/ log(2)約為2.3然后將j增加1而將i減小2將增加僅0.3。 那么問題是在每個階段找到B對於i和j的變化的最小可能增加。

為了做到這一點,我只保留了一個記錄,因為我增加了i和j的最有效變換(即從每個變換中添加和減去的變換)以獲得系列中可能的最小增量。 然后應用任何一個有效的(即確保i和j不變為負數)。

因為在每個階段你可以減少i或減少j,實際上有兩類變換可以單獨檢查。 新的轉換不一定要包含在我們未來的檢查中,只比其他同類產品更好。

為了測試我的思想,我在LinqPad中編寫了一個程序。 需要注意的一點是,Dump()方法只是將對象輸出到屏幕,並且語法/結構對於真正的c#文件無效。 如果你想運行它,轉換它應該很容易。

希望任何未明確解釋的內容都可以從代碼中理解。

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;
        }
    }

我沒有費心去看這個效率,但我強烈懷疑它比其他一些解決方案更好,因為在每個階段,我需要做的就是檢查我的變換集 - 計算出與“n”相比有多少變換是非平凡的。 它顯然是相關的,因為你越往前就會有更多的變換,但是新變換的數量在更高的數字時變得越來越小,所以也許它只是O(1)。 這個O東西總是讓我困惑。 ;-)

與其他解決方案相比,一個優點是它允許您計算i,j而無需計算產品,從而可以計算出序列的內容,而無需計算實際數字本身。

為了它在前230個nunmbers之后的價值(當int用完空間時)我每次都要進行9次變換檢查。 並且考慮到它只有我的總數溢出,我跑了,如果為第一百萬個結果,並得到i = 5191和j = 354。 變換的數量是23.該列表中該數字的大小約為10 ^ 1810。 達到這個水平的運行時間約為5秒。

PS如果你喜歡這個答案,請隨時告訴你的朋友,因為我花了很多年的時間和幾個+ 1s將是很好的補償。 或者實際上只是評論告訴我你的想法。 :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM