對於一個長度為n+1的多項式用數組存儲各項係數,其係數均為非負整數且小於10,則其可代表一個十進位數。 即[公式] 。同理也可以定義二進位、八進位、十六進位、三十六進位等。

對於一個A進位大數,對應數值相等的B進位大數是唯一的。有比較高效的從 [公式] 獲得 [公式] 的方法嗎?其中 [公式]


先解決其中的兩種特例,再通過和2進位相互轉換的方式解決其它情況。這裡默認用到的a或者b進位的係數本身的加減乘除運算都是O(1)的,不考慮2^1000-1進位這種情況。

第一種最簡單,如果a和b存在正整數m和n滿足 [公式] ,那麼這個轉換是很容易的,只需要a進位每m位轉換成b進位n位即可,這個應該很好理解,就不具體說了。

第二種我們考慮a和b互質的情況,這裡考慮應用歐拉定理,對於任意的正整數k,有:

[公式]

這裡 [公式] 是歐拉函數,代表1-n中與n互質的數的個數。注意到與 [公式] 互質的數一定與b也互質,很容易得到

[公式]

也就有

[公式]

我們可以將同餘式改寫成

[公式]

注意到有

[公式]

[公式] 本身用b進位表示的話,這個遞推式本身是需要乘法的,這裡需要應用多項式快速乘法的原理,將所有的b進位數都表示成FFT變換的形式(FFT的大小可以提前通過最大需要的位數估算出來),這樣多項式的b次方的計算可以通過FFT計算,複雜度可以降到 [公式] ,由於 [公式] 的位數不超過 [公式] ,當k為 [公式] 時算出 [公式][公式] 整體的複雜度也是 [公式]

現在,將一個n位a進位數A轉換為b進位,且a與b互質時,我們首先取k使得

[公式]

然後將這個數分解成後 [公式] 位和前面的位數,這兩個分解後的a進位數分別記為B和C,即

[公式]

根據前面的討論,有

[公式]

如果遞歸計算出B和B+C的b進位表達式,接下來同樣用FFT計算一次乘法,再移位、相加,額外的複雜度為 [公式] ,而B和C的長度不超過 [公式] ,整體複雜度應該不超過 [公式] 。如果中間保留FFT結果(B和C都遞歸計算出FFT結果),考慮到FFT結果可以以 [公式] 的時間複雜度進行乘法、加法、移位和擴張(點數加倍),整體複雜度可以降到 [公式] ,但是需要證明這種情況下進位處理的複雜度不會增加。


接下來我們來處理a和b不互質的情況,考慮到如果我們可以將所有進位的數都和2進位進行 [公式] 的相互轉換,那也就等於所有進位之間都可以互相轉換了,所以我們接下來的重點考慮和2進位的相互轉換。奇數進位、 [公式] 進位的情況前面解決了,我們接下來解決 [公式] ,其中q是大於或等於3的奇數的情況。

首先是a進位向2進位轉換,跟之前一樣我們有

[公式]

跟之前一樣寫成

[公式]

兩邊同時乘以一個係數有

[公式]

[公式]

剩下的和之前做法差不多,首先將n位數A分解為

[公式]

然後變成

[公式]

遞歸轉換B和C,然後通過相同的方法做乘法、移位、加法,就可以實現轉換。

接下來是2進位向a進位轉換,和之前一樣有

[公式]

寫成

[公式]

同時乘以 [公式]

[公式]

也就是

[公式]

之後也是分解

[公式]

[公式]

和剛才略有不同的是,這次是先移位相加,然後再分別轉換,但複雜度是一樣的。

至此,所有進位都可以和2進位相互轉換,因此所有進位之間也都可以進行相互轉換了。


參見 Numerical Recipes in C: The Art of Scientific Computing. Second Edition by William H. Press, Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery 第922頁,書裏實現的是 [公式] 的方法,但是書裏說了

NOTE: For simplicity, this routine implements a slow [公式] algorithm. Fast [公式] , more complicated, radix conversion algorithms do exist.

書裏前一段是這麼說的

It is possible to do the radix conversion as a fast operation by a "divide and conquer" strategy.

所以應該是用分治法


抱著學習的心態嘗試著答這道題目


思路

主體思路是分治,思路來源[1]

首先將A進位轉化為二進位:

  1. 將N位的數字 [公式] 分成兩半: [公式]
  2. 遞歸的將high, low兩部分轉化為二進位
  3. 計算 [公式]

那麼最後X就是二進位狀態,遞歸的時間複雜度為 [公式][公式] 用FFT計算複雜度為 [公式] ,( [公式] 轉化為二進位的時間複雜度咋算啊??)總複雜度為 [公式]

將二進位轉化為B進位:

  1. [公式] , M為表示的位數
  2. [公式]
  3. 遞歸去計算high, low部分,將兩者相合,即可得到答案。

總時間複雜度為 [公式]


這也是劍靈大佬評論區裡面的解法。

實現

GMP源碼?

github.com

我們主要學習mpn/generic/set_str.c (radix-&>binary) 和 mpn/generic/get_str.c (binary-&>radix) ,結合說明文檔[2] 。最大進位目前支持256進位。

並且如果字元串的大小沒有超過設置的閾值SET_STR_DC_THRESHOLD那麼就是用樸素的 [公式] 演算法 mpn_bc_xx() ,否則會使用分治演算法mpn_dc_xxx()


不知道有沒有OI/ACM大佬能不能把這個出成一個模板題 orz,跪求鏈接和板子

參考

  1. ^http://www.numberworld.org/y-cruncher/internals/radix-conversion.html
  2. ^https://gmplib.org/manual/Binary-to-Radix.html#Binary-to-Radix


這裡有些乾貨, 看了其他的答案,似乎沒什麼特別好的回答.

f(x) = a0*x^0 + a1*x^1 + a2*x^2 + a3*x^3 + a4*x^4 + ....

=B0*x^0 + B1*x^1 + B2*x^2 + B3*x^3 + B4*x^4 + …. (1)

= (B0 + B1*x^1)*x^0 + (B2+B3*x)*x^2 + (B4 + B5*x)*x^4 + …..

= C0*x^0 + C2*x^2 + C4*x^4 + ….. (2)

= D0*x^0 + D4*x^4 + …. (3)

.

.

.

= N0*x^0 + N(n/2) * x^(n/2) (t)

= M

簡單起見, 合理假設k * k 長度的乘法複雜度是 k logk

複雜度計算位

(1) (2) (3) ….(t)

每行的複雜度為 N / k * k logk = N * logk

共 logN行

O(N) = N * log1 + N * log2 + N * log4 + N * log8 + ….NlogN

= N * LogN * (1/LogN + ….1 / 4 + 1/3 + 1/2 + 1/1) (準確值)

= N*logN

PS:

這裡有幾個地方是是有較大的修正:

1)在實際工程上, (1/LogN + ….1 / 4 + 1/3 + 1/2 + 1/1) 這一坨 這裡N 可以認為不可能大於 2^32次方, 因此這裡基本上就是一個很小的常數K,

2)由於以上的計算只是簡單地把 k * k位乘法看成 klogk的乘法,實際上,這個估算是估算大了,如果k不大的情況下,k * k 有比klogk的FFT更好的演算法.

3)每一層 * x^k這個乘法,由於 *x^K在每層都是固定的, 實際上FFT裡面的3次傅裏葉變換,有一次傅裏葉變換在每層裡面都是可以共用的. 因此 每次* x^k 實際上平均下來相當於只要2次變換多一些就可以了

因此這個

(準確值) 裡面的 NlogN 的係數實際上並不大.

另外,

這個演算法跟多少進位轉換到多少進位無關.


如果這個問題的答案有了,你是不是就想顛覆現有計算機的數學架構哦?

從這個數可以確定一組泛函。轉換前後的兩個不同數制的數組,分別以整數的形式落在兩個一元多項式上,如果可以快速轉換,各種不同的數制平行計算即可實現,我理解這實際上就是量子模擬計算。

從實數出發,這一想法已經被5次以上無根式解鎖定,現在就看有沒有其它捷徑了。


以二進位轉十進位為例:

已知大數 B0,可以通過貪心演算法得到小於但是接近 B0 的十進位數 D0 (10^n, n = 2^x)

將 B0 轉成 D0 進位的 B1

將 B1 轉成 D1 (10^(n / 2)) 進位的 B2

...

按上述方式迭代至 Dm = 10 得到的 Bm 為期望解,且符合期望時間複雜度

...

突然剛發現 貪心演算法部分需要用到 n*n 複雜度的乘法,乖乖用模板元生查找表成然後二分查找吧


如果假設A,B是常數, [公式] ,首先找到 [公式] 並轉換成 [公式] 進位,那麼 [公式] ,於是分治的複雜度是 [公式]


推薦閱讀:
相關文章