簡體   English   中英

打印非零數字按升序排列的所有數字

[英]Print all numbers whose nonzero digits are in ascending order

我正在嘗試編寫一個程序,該程序將多個數字和一個基數作為參數,並通過按升序排列非零數字的數字向上計數。 例如,在帶有3位數的基數4中,它應該打印:

000 001 002 003 010 011 012 013 020 022 023 030 033 100 101 102 103 110 111 112 113 120 122 123 130 133 200 202 203 220 222 223 230 233 300 303 330 333

在4位數的基數3中應該打印:

0000 0001 0002 0010 0011 0012 0020 0022 0100 0101 0102 0110 0111 0112 0120 0122 0200 0202 0220 0222 1000 1001 1002 1010 1011 1012 1020 1022 1100 1101 1102 1110 1111 1112 1120 1122 1200 1202 1220 1222 2000 2002 2020 2022 2200 2202 2220 2222

我已成功完成此操作,但我的算法似乎不必要地復雜和耗時(時間對我的應用程序非常重要)。 有沒有辦法讓它更快,或者如果速度無法提高就簡化它?

這是程序:

public static void count(int base, int size)
{
    int[] array = new int[size];
    print(array); // private print method prints out the array
    int index = 0;
    while (index < array.length)
    {
        if (array[index] < base - 1)
        {
            // check whether we need to increase array[index] by extra to maintain the order
            if (array[index] == 0)
            {
                int i;
                // search for the next nonzero digit
                // this search seems to take unnecessary time; is there a faster alternative?
                for (i = index + 1; i < array.length && array[i] == 0; i++);

                // check whether there was, in fact, some later nonzero digit
                if (i < array.length) array[index] = array[i];
                else                  array[index] = 1;
            }

            else array[index]++;

            print(array);
            index = 0;
        }

        // carry over to the next digit
        else array[index++] = 0;
    }
}

我會尋求一個遞歸的解決方案:

public static void count(int base, int size) {
    int[] configuration = new int[size];
    placeDigits(configuration, base, 0, 1);
}

public static void placeDigits(int[] configuration, int base, int pos, int minNonZero) {
    if (pos >= configuration.length) {
        print(configuration);
    } else {
        // 0 is a possible candidate
        configuration[pos] = 0;
        placeDigits(configuration, base, pos + 1, minNonZero);
        // digits between minNonZero and base
        for (int d = minNonZero; d < base; d++) {
            configuration[pos] = d;
            placeDigits(configuration, base, pos + 1, d);
        }
    }
}

它將數字一個接一個地放入數組中,並觀察到非零數字必須不減少的約束。

好吧,這有點作弊,但這是一個以偽代碼表示的解決方案:

results : list
for i in 1..max
   if '0' not in str(i)
       append i to results
   fi
rof
print results

另一方面, 是騙子嗎? “具有非零數字的數字”本質上是關於數字的十進制表示的問題,而不是某種意義上的數字本身。

時間復雜度當然是On ) - 至少將str(i)為一個步驟,這是一個有點作弊的地方。

只是為了好玩,這是Python中的相同解決方案:

print [i for i in xrange(max) if '0' not in str(i)]

以及遞歸解決方案的草圖:

dig成為非零數字的列表,即['1','2','3','4','5','6','7','8','9'] 枚舉該長度ceil(log10(max))列表中的所有字符串ceil(log10(max)) (測驗問題,為什么要限制?)。

按順序打印這些字符串,超過max時停止。

如果您不介意將數字保存在內存中,則可以編寫以下算法:

  1. 從數字0,1 ... base-1開始
  2. 對於每個添加的數字, d ,首先添加零,然后添加以數字d或更高開頭的所有先前數字(通過起始數字和數字編號索引,您可以直接訪問它們)。

或者,有些人喜歡短語, dp樣式:讓dp[i][j]表示帶有i位數和最左邊數字j數字序列。 然后dp[i][j] = [d] ++ map (d +) dp[l][k], for all l < i and k >= j, where d = j * 10 ^ (i - 1)

(我從Haskell借用了++ ,它通常意味着連接列表)。

例如,基數4,數字3:

Start with one digit:
0,1,2,3

Add to the second digit from the first sequence:
10,11,12,13
20,22,23
30,33

Third digit, add from all previous sequences:
100,101,102,103
110,111,112,113
120,122,123
130,133

200,202,203
220,222,223
230,233

300,303
330,333

JavaScript代碼:

var base = 4;

var dp = [,[]];

for (var j=0; j<base; j++){
  dp[1][j] = [j];
}

for (var i=2; i<4; i++){
  dp[i] = [];
  for (var j=1; j<base; j++){
    var d = j * Math.pow(10,i - 1);
    dp[i][j] = [d];
    for (var l=1; l<i; l++){
      for (var k=j; k<base; k++){
        dp[i][j] = dp[i][j].concat(
                     dp[l][k].map(function(x){
                       return d + x;
                     }));
      }
    }
  }
}

console.log(JSON.stringify(dp))

/*
 [null,[[0],[1],[2],[3]]
,[null,[10,11,12,13]
,[20,22,23]
,[30,33]]
,[null,[100,101,102,103,110,111,112,113,120,122,123,130,133]
,[200,202,203,220,222,223,230,233]
,[300,303,330,333]]]
*/

你寫的很有趣的程序。

我試圖提高嵌套搜索的性能,但到目前為止,我還沒有找到一種方法來查找搜索下一個小於O(n)的非零數字的最壞情況。

在最壞的情況下,子陣列A [i..array.length-1]沒有排序,並且array [i] = 0,因此要找到下一個非零數字,你必須進行線性搜索。

另外,如果沒有下一個非零數字,則必須搜索整個數組以“找到它”。

(例如:對於序列'0040'我們有i = 1.子陣列[0,4,0]沒有排序,所以你必須進行線性搜索才能找到下一個最大/最小的非零數字,這將是位於數組[2])

最壞情況的復雜性將是O(n)。

你能改善跑步時間嗎? 我想如果你做一些並行編程你可以,但不幸的是我不知道那個領域可以幫助你。

我沒有測量性能,但認為我的代碼更易讀。 我們的想法是,通過整數迭代從0到十進制的已知數生成每個基數b和長度l,使用Java內置轉換十進制到基數b,然后刪除該數字中的零(這是類型為String)並測試升序。

輸出必須用零填充,因此最后是復雜的printf。

public static boolean isAscending (String digits) {
    for (int i = 1; i < digits.length (); ++i)
        if (digits.charAt (i-1) > digits.charAt (i)) 
            return false;
    return true;
}

public static void count (int base, int size)
{
    /** 
        Build numbers,i.e. from 000 to 333, for base 4 at length 3
        or 4^3 = 4*4*4 = 64 combinations 
    */      
    double max = Math.pow (base, size);
    for (int i = 0; i < max; ++i)
    {
        String res = Integer.toString (i, base);
        if (isAscending (res.replaceAll ("0", "")))
            System.out.printf ("%0"+size+"d ", Long.parseLong (res)); 
    }
}

這個遞歸函數試圖避免任何不必要的循環

 public static void count0(int min, int ptr)
 {
      int me = 0; // joker
      do {
            array[ptr] = me;
            if (ptr > 0) count0(Math.max(me,min), ptr-1);
            else print(array);
            me = me == 0 ? (min > 0 ? min : 1) : me+1;
      } while (me < base);
 }

這樣調用(長度為17的基數為8)來攜帶較少的參數:

 static int array[];
 static int base;

      int leng = 17;
      base = 8;
      array = new int [leng];

      count0 (0, array.length-1);

然而,遞歸有其代價。

遲到了這個更快的答案:

Base               8
Size              20 digits
Current solution: 79 seconds (76~82)
Solution below:   23 seconds (22~24)
Possible numbers: 12245598208

沒有印刷品。 原理:

規則“一個數字后跟一個0或一個數字> =前面的數字”對於(有效的)數字組也是有效的:“一個組后面可以跟一組零,或者一個小數字組是> =前面各組中的任何一個“。 處理在組級別完成,而不是在數字級別完成。

給定T總大小,並且每組中N個較小的位數(T%N == 0),通過計算所有可能的N個數字組,然后可以將它們組合在一起(每個解決方案的T / N組)。

  • 在數組中預先計算較小尺寸的所有可能數字,例如5(2668個數字)(不到半秒)
  • 保持另一個數組中每個“部分”的最大數字
  • 在另一個“atleast”數組中設置基於其較小數字的組的索引
  • 通過粘貼所有可能的塊(例如4x5)來構建大數,只要組的低位必須> =前面組中的最高位。

用於預先計算小塊(部件)的示例代碼

 static ArrayList<int[]> parts = new ArrayList<int[]>();
 static ArrayList<ArrayList<Integer>> atleast = new ArrayList<ArrayList<Integer>>();
 static ArrayList<Integer> maxi = new ArrayList<Integer>();
 static int stick[];
 static int base;
 static long num = 0;

 public static void makeParts(int min, int ptr)
 {
      int me = 0;
      do {
            array[ptr] = me;
            if (ptr > 0) makeParts(Math.max(me,min), ptr-1);
            else {
                 // add part
                 int[] newa = new int [array.length];
                 int i,mi,ma,last=array.length-1;
                 for (i=0 ; i<array.length ; i++) newa[i] = array[i];
                 parts.add(newa);
                 // maxi
                 for (i=0 ; i<=last && newa[i]==0 ; i++) /* */;
                 maxi.add(ma = i<=last ? newa[i] : 0);
                 // mini
                 for (i=last ; i>=0 && newa[i]==0 ; i--) /* */;
                 mi = i>=0 ? newa[i] : 0;
                 // add to atleast lists
                 int pi = parts.size() - 1;
                 ArrayList<Integer> l;
                 int imi = mi == 0 ? base-1 : mi;
                 for (i=0 ; i<=imi ; i++) {
                      if (i < atleast.size()) l = atleast.get(i);
                      else {
                            l = new ArrayList<Integer>();
                            atleast.add(i, l);
                      }
                      l.add(pi);
                 }
            }
            me = me == 0 ? (min > 0 ? min : 1) : me+1;
      } while (me < base);
 }

堅持“部分”

 public static void stickParts(int minv, int ptr)
 {
      // "atleast" gives indexes in "parts" of groups which min digit
      // is at least "minv" (or only zeroes)
      for (int pi: atleast.get(minv)) {
            stick[ptr] = pi;
            if (ptr > 0) {
                 stickParts(Math.max(minv,maxi.get(pi)), ptr-1);
            }
            else {
                 // count solutions
                 // the number is made of "parts" from indexes
                 // stored in "stick"
                 num++;
            }
      }
 }

在“主要”中調用此

      base = 8;
      int leng  = 20;
      int pleng =  4;

      array = new int [pleng];

      makeParts(0,array.length-1);

      num = 0;
      stick = new int [leng / pleng];
      stickParts(0, (leng/pleng) - 1);

      out.print(String.format("Got %d numbers\n", num));

例如,如果T(總大小)是素數,則必須計算另一個特定組,例如對於大小17,我們可以有3組(5位數)+一組兩位數。

暫無
暫無

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

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