Super Pow:如何高效進行模冪運算

2021-02-15 labuladong

點擊上方藍字設為星標

今天來聊一道與數學運算有關的算法題目,LeetCode 372 題 Super Pow,讓你進行巨大的冪運算,然後求餘數。

int superPow(int a, vector<int>& b);

要求你的算法返回冪運算a^b的計算結果與 1337 取模(mod,也就是餘數)後的結果。就是你先得計算冪a^b,但是這個b會非常大,所以b是用數組的形式表示的。

這個算法其實就是廣泛應用於離散數學的模冪算法,至於為什麼要對 1337 求模我們不管,單就這道題可以有三個難點:

一是如何處理用數組表示的指數,現在b是一個數組,也就是說b可以非常大,沒辦法直接轉成整型,否則可能溢出。你怎麼把這個數組作為指數,進行運算呢?

二是如何得到求模之後的結果?按道理,起碼應該先把冪運算結果算出來,然後做% 1337這個運算。但問題是,指數運算你懂得,真實結果肯定會大得嚇人,也就是說,算出來真實結果也沒辦法表示,早都溢出報錯了。

三是如何高效進行冪運算,進行冪運算也是有算法技巧的,如果你不了解這個算法,後文會講解。

那麼對於這幾個問題,我們分開思考,逐個擊破。

如何處理數組指數

首先明確問題:現在b是一個數組,不能表示成整型,而且數組的特點是隨機訪問,刪除最後一個元素比較高效。

不考慮求模的要求,以b = [1,5,6,4]來舉例,結合指數運算的法則,我們可以發現這樣的一個規律:

看到這,我們的老讀者肯定已經敏感地意識到了,這就是遞歸的標誌呀!因為問題的規模縮小了:

    superPow(a, [1,5,6,4])
=>  superPow(a, [1,5,6])

那麼,發現了這個規律,我們可以先簡單翻譯出代碼框架:

// 計算 a 的 k 次方的結果
// 後文我們會手動實現
int mypow(int a, int k);

int superPow(int a, vector<int>& b) {
    // 遞歸的 base case
    if (b.empty()) return 1;
    // 取出最後一個數
    int last = b.back();
    b.pop_back();
    // 將原問題化簡,縮小規模遞歸求解
    int part1 = mypow(a, last);
    int part2 = mypow(superPow(a, b), 10);
    // 合併出結果
    return part1 * part2;
}

到這裡,應該都不難理解吧!我們已經解決了b是一個數組的問題,現在來看看如何處理 mod,避免結果太大而導致的整型溢出。

如何處理 mod 運算

首先明確問題:由於計算機的編碼方式,形如(a * b) % base這樣的運算,乘法的結果可能導致溢出,我們希望找到一種技巧,能夠化簡這種表達式,避免溢出同時得到結果。

比如在二分查找中,我們求中點索引時用(l+r)/2轉化成l+(r-l)/2,避免溢出的同時得到正確的結果。

那麼,說一個關於模運算的技巧吧,畢竟模運算在算法中比較常見:

(a*b)%k = (a%k)(b%k)%k

證明很簡單,假設:

a=Ak+B;b=Ck+D

其中 A,B,C,D 是任意常數,那麼:

ab = ACk^2+ADk+BCk+BD

ab%k = BD%k

又因為:

a%k = B;b%k = D

所以:

(a%k)(b%k)%k = BD%k

綜上,就可以得到我們化簡求模的等式了。

換句話說,對乘法的結果求模,等價於先對每個因子都求模,然後對因子相乘的結果再求模

那麼擴展到這道題,求一個數的冪不就是對這個數連乘麼?所以說只要簡單擴展剛才的思路,即可給冪運算求模:

int base = 1337;
// 計算 a 的 k 次方然後與 base 求模的結果
int mypow(int a, int k) {
    // 對因子求模
    a %= base;
    int res = 1;
    for (int _ = 0; _ < k; _++) {
        // 這裡有乘法,是潛在的溢出點
        res *= a;
        // 對乘法結果求模
        res %= base;
    }
    return res;
}

int superPow(int a, vector<int>& b) {
    if (b.empty()) return 1;
    int last = b.back();
    b.pop_back();

    int part1 = mypow(a, last);
    int part2 = mypow(superPow(a, b), 10);
    // 每次乘法都要求模
    return (part1 * part2) % base;
}

你看,先對因子a求模,然後每次都對乘法結果res求模,這樣可以保證res *= a這句代碼執行時兩個因子都是小於base的,也就一定不會造成溢出,同時結果也是正確的。

至此,這個問題就已經完全解決了,已經可以通過 LeetCode 的判題系統了。

但是有的讀者可能會問,這個求冪的算法就這麼簡單嗎,直接一個 for 循環累乘就行了?複雜度會不會比較高,有沒有更高效的算法呢?

有更高效的算法的,但是單就這道題來說,已經足夠了。

因為你想想,調用mypow函數傳入的k最多有多大?k不過是b數組中的一個數,也就是在 0 到 9 之間,所以可以說這裡每次調用mypow的時間複雜度就是 O(1)。整個算法的時間複雜度是 O(N),N 為b的長度。

但是既然說到冪運算了,不妨順帶說一下如何高效計算冪運算吧。

如何高效求冪

快速求冪的算法不止一個,就說一個我們應該掌握的基本思路吧。利用冪運算的性質,我們可以寫出這樣一個遞歸式:

這個思想肯定比直接用 for 循環求冪要高效,因為有機會直接把問題規模(b的大小)直接減小一半,該算法的複雜度肯定是 log 級了。

那麼就可以修改之前的mypow函數,翻譯這個遞歸公式,再加上求模的運算:

int base = 1337;

int mypow(int a, int k) {
    if (k == 0) return 1;
    a %= base;

    if (k % 2 == 1) {
        // k 是奇數
        return (a * mypow(a, k - 1)) % base;
    } else {
        // k 是偶數
        int sub = mypow(a, k / 2);
        return (sub * sub) % base;
    }
}

這個遞歸解法很好理解對吧,如果改寫成迭代寫法,那就是大名鼎鼎的快速冪算法。至於如何改成迭代,很巧妙,這裡推薦一位大佬的文章 讓技術一瓜共食:快速冪算法。

雖然對於題目,這個優化沒有啥特別明顯的效率提升,但是這個求冪算法已經升級了,以後如果別人讓你寫冪算法,起碼要寫出這個算法。

至此,Super Pow 就算完全解決了,包括了遞歸思想以及處理模運算、冪運算的技巧,可以說這個題目還是挺有意思的,你有什麼有趣的題目,可以留言分享一下。

歷史文章:

回溯算法團滅排列/組合/子集問題

加密算法的前世今生

這個問題不簡單:尋找缺失元素

相關焦點

  • 利用Express Tool工具的superhatch來自定義填充圖案
    Express toolsI提供了一個高效直觀的自定義圖案的工具superhatch。*NET3,網狀圖案 0-60-1200, 0, 0,0, 3.17560, 0, 0,0, 3.175120, 0,0, 0, 3.175好在express tool為我們提供了superhatch工具。
  • 使用Python super方法為您的類增強
    在本教程中,您將了解以下內容:Python中的繼承概念Python中的多重繼承super()功能如何工作super()單繼承中的函數如何工作super()多繼承中的函數如何工作免費獎勵: 關於Python掌握的5個想法,Python開發人員的免費課程,向您展示將Python技能提升到新水平所需的路線圖和思維模式。
  • 韓國TMTG上線 Coinsuper交易所!
    TMTG將於香港時間8月16日08:08:08pm上線香港領先的數字資產交易平臺Coinsuper(幣成),開放邁達斯金(TMTG)的充值/提現功能,同步開啟TMTG/BTC交易。 Coinsuper交易所網址:https://www.coinsuper.com/  什麼是TMTG?
  • Java之super關鍵字的用法
    各為小夥伴們大家好,這次小編要介紹的是Java當中super關鍵字的用法,在上面的文章中小編有講過,super關鍵字是用來調用父類之間的成員變量和成員方法。現在小編來總結一下super關鍵字的用法。具體如下:1.在子類的成員方法中,訪問父類的成員變量。
  • Nature Nanotechology:如何高效利用太陽能進行水汽蒸發
    如此高效的太陽能蒸髮結構是由親水聚合物框架(聚乙烯醇,PVA)和太陽能吸收劑(還原氧化石墨烯,還原氧化石墨烯)組成的混合水凝膠體系,其內部有毛細管通道。PVA可以大大促進水的蒸發,這是由於水凝膠中的網絡降低了蒸發焓。還原氧化石墨烯貫穿於聚合物網絡使之成高效吸收太陽能。毛細管通道維持著產生連續蒸汽需要的充足供水。這種以水凝膠為基礎的太陽能蒸發器也顯示出了很有前途的防汙性能,使長期的海水淡化無需循環。
  • 孿生網絡如何識別面部相似度?這有篇PyTorch教程
    代碼片段:孿生網絡架構: class SiameseNetwork(nn.Module): def __init__(self): super(SiameseNetwork, self).
  • LeetCode-50.求X的N次方(Pow(x, n))
    Pow(x, n)實現 pow(x, n) ,即計算 x 的 n 次冪函數。
  • 【聽力】What It Would Be Like to Live on a Super Earth
    They’re called super-Earths.Super-Earths may be some of the most common planets in our galaxy.Since 2009, Kepler Space Telescope has discovered about 4,000 exoplanets. 30% of them are super-Earths.
  • 如何進行高效的英文閱讀
    首先來說如何選書,我們讀中文書的目的各不相同,但是大多數人讀英文書的目的都是相同的,那就是提升英語水平。「提升」這個詞是什麼意思呢?就是在原來的基礎上有所增長。請大家注意,是在原來的基礎上進行增長,所以,在選一本英文書的時候,我們要知道自己原來的基礎在哪,也就是要選擇一本適合自己難度的英文書。
  • 學術乾貨丨如何使用PS進行SCI組圖
    來源丨超級super棟 之前我們講解了如何使用AI進行SCI組圖,其特點是在同一畫板上進行圖片的排列組合。那麼PS則是在不同圖層上進行圖片的整合。這兩個軟體各有各的好處,會熟練使用其中一個即可。那麼今天主要來講解一下,如何使用PS將多張圖組合在一起。
  • 在Python中使用super函數,以及多重繼承和組合
    [ 使用 super 函數]super 函數能夠自動找到基類的方法,而且還傳入了 self參數:代碼進行如下修改class Shark(Fish):def _ _init_ _(self):super(). _ _init
  • 給訓練踩踩油門:編寫高效的PyTorch代碼技巧
    它可以讓需要採用梯度下降算法進行訓練的機器學習算法的實現更加方便,可以更高效的自動計算函數的梯度。我們的目標是提供更好的 PyTorch 介紹以及討論使用 PyTorch 的一些最佳實踐。對於 PyTorch 第一個需要學習的就是張量(Tensors)的概念,張量就是多維數組,它和 numpy 的數組非常相似,但多了一些函數功能。
  • MYC基因通過超級增強子實現高效出核
    MYC基因通過超級增強子實現高效出核 作者:小柯機器人 發布時間:2019/12/2 13:31:31 瑞典卡羅林斯卡大學醫院Rolf Ohlsson、Anita Göndör等研究人員合作發現
  • 手機屏幕大比拼,OLED,AMOLED,super AMOLED哪款屏幕更好呢?
    在市面上大家見到最多的手機屏幕大概就是LCD屏幕,OLED屏幕,AMOLED屏幕和super AMOLED屏幕了吧,今天就後三種和大家談論一下屏幕的優勢和劣勢。首先先說一下屏幕之間的關係,其實AMOLED屏幕和super AMOLED屏幕都可以統稱為OLED屏幕,那麼為什麼不統一稱為OLED屏呢,其實就和手機一樣有高端旗艦,中端旗艦和千元機之分,將層次劃得更清晰,只是為了對價格的區分更明顯。
  • 超臨界水氣化技術:生物質高效高產制氫新方向|MDPI
    因此,高效、環保和廉價的大規模製氫技術是全世界研究的熱點問題。生物質能是一種可再生能源,故而生物質制氫技術被認為是如今最有前景的制氫方式。 超臨界水氣化(Supercritical Water Gasification, SCWG) 是20世紀70年代中期由美國麻省理工學院 (MIT) 的Modell提出的新型制氫技術。
  • 研究團隊通過特製鋁板和太陽能來高效淨水
    其特點是藉助一種特殊設計的鋁板,通過太陽能來實現高效淨水。鑑於太陽能本身就可以消滅水生病原體,且能夠為低成本的淨水器供電,這套方案還是相當可行的。Sustainable water purification using laser treated metal(via)這使得材料具有高吸收和「超芯吸」(super-wicking)性能,即便鋁板未將鋁板完全浸沒在水中,亦可抵抗一定的重力,從儲液罐中沿金屬表面吸出一層水。
  • 世界三文魚企業最新排名出爐,Agrosuper位居第五
    世界三文魚企業最新排名出爐,Agrosuper位居第五2019-07-09 09:09:00  水產養殖網2017年排名智利第一的三文魚生產商Salmones&nbspMultiexport降至第三,第一的位置被Agrosuper(新AquaChile)替代。去年,AquaChile公司併購Salmones&nbspMagallanes公司,後又被Agrosuper以8.5億美元收購,合併為智利最大的三文魚企業。
  • 疫情過後,眼鏡店人員該如何進行高效工作?
    疫情過後,人們的身心都陷入疲憊的狀態,但是只要工作起來,我們的精神和活力就會大增,下面雲鏡姐姐來跟眼鏡行業的朋友們說說,我們在疫情過後,該如何進行高效工作。一.整理商品、檢查標籤並檢查商品 對貨架上各種形式陳列的商品進行分類整理。要豐滿、美觀、易購,不得空置。檢查價格標籤,確認商品價格一致,商品標識到位,商品逐一籤字。在整理貨物時,我們應該仔細檢查貨物的質量。如果發現有損壞的商品,應將其移走並進行處理,以維護消費者的利益和本店的良好形象。推銷員必須把它做好。6.
  • Super moon to appear tonight! 今晚公眾可觀賞超級月亮
    (via: Tencent)Tonight is a good occasion to see the super moon.A super moon is a full moon or a new moon that approximately coincides with the closest distance that the Moon reaches to Earth in its elliptic orbit, resulting in a larger-than-usual apparent size of