摘要:一個來自于程序設計的經典問題。注意事項負數問題。和上一點是一樣的問題,要確定方式是屬于具體的對象,還是屬于一個類。
1.初見一個來自于C++程序設計的經典問題。如何定義一個分數類,實現分數的約分化簡,分數之間的加法、減法、乘法、除法四則運算?
剛看到這道題的時候,第一感覺是挺簡單的啊,就是基本的面向對象,定義對應的加減乘除類就可以了啊,然而到了實現的時候才發現許多問題是說起來容易做起來難,在實現的過程中,發現了許多的注意點,以及算法。
最終得出結論:這個問題著實是考察程序員基本功的一道好題。
2.整體思路分數類設計的總體思路如下:
首先是分數的表示,這就需要利用兩個變量保存分數的分子和分母;
其次是約分和通分,由于分數四則運算中需要借助約分和通分來實現,因此必須先考慮實現這兩個算法。
加法和乘法的實現。利用約分和通分就可以輕松實現。
減法和除法的實現。是加法和乘法的逆運算。和直接轉化為加法和乘法。
3.注意事項負數問題。這個問題十分重要,在我們的算法中,都規定分母為正數,如果出現了分母為負數的情況,就分子分母同時乘以-1,把負數運算放在分子上。
函數的副作用(side effect)。盡量不要在方法中改變原來分數的值,否則會產生副作用,導致后面的運算出錯,在代碼中會說明。
靜態方法和動態方法。和上一點是一樣的問題,要確定方式是屬于具體的對象,還是屬于一個類。
除法運算中,除數不能等于0
4.代碼實現 4.1 屬性和構造方法構造方法中有三個細節:一是使用了參數默認值,默認不寫參數時,讓分子分母都等于1;二是在構造方法中進行了分母合法性的驗證,分母等于0時直接返回錯誤信息;三是對負值進行了處理,使分數的分母永遠為正數,方便后續運算。
/** * 分數運算類 */ class Fraction { //定義分子和分母 public $fenzi; public $fenmu; //構造函數 function __construct($fenzi = 1,$fenmu = 1) { if ($fenmu == 0) { return "分母不能為0"; } if ($fenmu < 0) { $fenmu = -$fenmu; $fenzi = -$fenzi; } $this->fenzi = $fenzi; $this->fenmu = $fenmu; } }4.2 最大公約數和最小公倍數
為了后續的約分和通分,必須先求出最大公約數和最小公倍數。求最大公約數采用輾轉相除法,而最小公倍數由以下公式可求:
最小公倍數 = (數A * 數B)/ 最大公約數
//求最大公約數用于約分 private static function _getmax($a, $b) { if($a < 0) $a = -$a; if($b < 0) $b = -$b; $tmp = $a % $b; while($tmp != 0) { $a = $b; $b = $tmp; $tmp = $a % $b; } return $b; } //求最小公倍數用于通分 private static function _getmin($a, $b) { if($a < 0) $a = -$a; if($b < 0) $b = -$b; $max = self::_getmax($a,$b); $min = intval(($a * $b) / $max); return $min; } /** * 約分運算,基本算法為分子分母同時除以最大公約數; * @return void 將對象的分子分母約分為最簡形式 */ public function reduction() { $max = $this->_getmax($this->fenzi,$this->fenmu); $this->fenzi = intval($this->fenzi / $max); $this->fenmu = intval($this->fenmu / $max); }
這兩個方法全部都定義為靜態私有方法,只在類內調用且不需實例化。求最大公約數和最小公倍數的算法其實還有很多種,@燼醬采用了另外一種方法,C++代碼如下:
/** * 求最大公約數并進行約分 * @return void */ int reduction() { int i,comdiv,small,max; if(above1;i--) { if(small%i==0 &max%i==0 ) break; } comdiv=i; //最大公約數 if(i!=0) { above/=i; below/=i; } return 0; }
這種方法的本質就窮盡法,核心思想在于for循環當中。同樣的,也可用此法求最小公倍數。
4.3 分數加減分數加法的算法如下:
/** * 加法運算,寫成靜態方法,需要傳遞兩個分數對象實例。加法的基本步驟為: * 1. 求兩個分母的最小公倍數; * 2. 利用最小公倍數進行通分,此時分母就是最小公倍數,第一個分數的分子等于原來的分子*(最小公倍數/原來的分母),第二個分數的分子同理; * 3. 分母不變,分子相加; * 4. 對結果進行約分; * * @param fraction $fra1 分數相加的加數1 * @param fraction $fra2 分數相加的加數2 * @return fraction $fra 分數相加的計算結果 */ public static function add($fra1, $fra2) { $fra = new Fraction(); $min = self::_getmin($fra1->fenmu,$fra2->fenmu); $fenzi_left = $fra1->fenzi * ($min / $fra1->fenmu); $fenzi_right = $fra2->fenzi * ($min / $fra2->fenmu); $fra->fenmu = $min; $fra->fenzi = $fenzi_left + $fenzi_right; $fra->reduction(); return $fra; } /** * 減法運算,加法的逆運算,只需要將參數$fra2的分子取反,將減法運算化為加法運算 * * @param fraction $fra1 分數相減的被減數 * @param fraction $fra2 分數相減的減數 * @return fraction $fra 分數相減的計算結果 */ public static function minus($fra1, $fra2) { $fra_t = new Fraction(-$fra2->fenzi,$fra2->fenmu); return self::add($fra1,$fra_t); }
在上述算法中,定義了兩個靜態方法,每個方法需要傳入兩個分數對象,之后就可以按上面的算法步驟進行加法和減法運算了。其中減法運算只需要轉換為加法即可。需要注意的是,在減法運算中,存在兩種可能的寫法:
【寫法1】
$fra2->fenzi = -$fra2->fenzi;
【寫法2】
$fra_t = new Fraction(-$fra2->fenzi,$fra2->fenmu);
其中,第一種寫法直接改變了減數分子的值,這里對減法本身的結果不會造成影響,表面上看是成立的,但其實這種寫法產生了副作用,在計算乘法時,fra2就已經不是最初的分數值了,因此我們需要new一個新的對象,如寫法2所示,這樣就不會產生副作用改變分數2的值。
4.4 分數乘除分數乘數就比較簡單了,如下所示:
/** * 乘法運算,分子相乘,分母相乘之后再約分 * * @param fraction $fra1 分數相乘的乘數1 * @param fraction $fra2 分數相乘的乘數2 * @return fraction $fra 分數相乘的計算結果 */ public static function multiply($fra1,$fra2) { $fra = new Fraction(); $fenzi = $fra1->fenzi * $fra2->fenzi; $fenmu = $fra1->fenmu * $fra2->fenmu; $fra->fenzi = $fenzi; $fra->fenmu = $fenmu; $fra->reduction(); return $fra; } /** * 除法運算,乘法運算的逆運算,只需要將參數$fra2的分子分母調換,將除法運算化為乘法運算 * * @param fraction $fra1 分數相除的被除數 * @param fraction $fra2 分數相除的除數 * @return fraction 分數相除的計算結果 */ public static function divide($fra1,$fra2) { $fra_t = new Fraction($fra2->fenmu,$fra2->fenzi); $fra = self::multiply($fra1,$fra_t); return $fra; }4.5 分數的表示
最后,我們需要寫一個方法,把分數以a/b的形式打印出來。
public function display() { printf("%d/%d ",$this->fenzi,$this->fenmu); }
這樣我們的分數類的定義完了。
5. 結果展示下面展示運行結果,先寫一個調用:
$fra1 = new Fraction(3,4); $fra1->display(); $fra2 = new Fraction(12,20); $fra2->reduction(); $fra2->display(); $fra3 = Fraction::add($fra1,$fra2); $fra3->display(); $fra4 = Fraction::minus($fra1,$fra2); $fra4->display(); $fra5 = Fraction::multiply($fra1,$fra2); $fra5->display(); $fra6 = Fraction::divide($fra1,$fra2); $fra6->display();
如上,fra1期望直接打印出3/4, fra2對12/20先約分再輸出,期望是3/5,fra3是計算fra1和fra2的加法,fra4為減法,fra5為乘法,fra6為除法。結果如下所示:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/30348.html
摘要:最后,我們顯示了若干張圖像中所生成的趣味字幕。圖所提出的有趣字幕生成的體系結構。我們將所提出的方法稱為神經玩笑機器,它是與預訓練模型相結合的。用戶對已發布的字幕的趣味性進行評估,并為字幕指定一至三顆星。 可以毫不夸張地說,笑是一種特殊的高階功能,且只有人類才擁有。那么,是什么引起人類的笑聲表達呢?最近,日本東京電機大學(Tokyo Denki University)和日本國家先進工業科學和技...
摘要:本文討論了多個評估指標,并從多個方面對評估指標進行了實驗評估,包括距離分類器。鑒于定性評估的內在缺陷,恰當的定量評估指標對于的發展和更好模型的設計至關重要。鑒于評估非常有難度,評估評估指標則更加困難。 作者:Qiantong Xu、Gao Huang、Yang Yuan、Chuan Guo、Yu Sun、Felix Wu、Kilian Weinberger生成對抗網絡的評估目前仍以定性評估和...
摘要:我們在前文中考慮的那張圖就來自這篇文章,之后我們會用剪枝算法來改進之前的解決方案。剪枝算法的實現接下來討論如何修改前面實現的算法,使其變為剪枝算法。現在我們已經有了現成的和剪枝算法,只要加上一點兒細節就能完成這個游戲了。 前段時間用 React 寫了個2048 游戲來練練手,準備用來回顧下 React 相關的各種技術,以及試驗一下新技術。在寫這個2048的過程中,我考慮是否可以在其中加...
摘要:我們在前文中考慮的那張圖就來自這篇文章,之后我們會用剪枝算法來改進之前的解決方案。剪枝算法的實現接下來討論如何修改前面實現的算法,使其變為剪枝算法。現在我們已經有了現成的和剪枝算法,只要加上一點兒細節就能完成這個游戲了。 前段時間用 React 寫了個2048 游戲來練練手,準備用來回顧下 React 相關的各種技術,以及試驗一下新技術。在寫這個2048的過程中,我考慮是否可以在其中加...
摘要:如果做推薦系統不知道基于物品的協同過濾,那等同于做程序員不懂得冒泡排序。基于物品的八卦基于物品的協同過濾算法誕生于年,是由亞馬遜首先提出的,并在年由其發明者發表了相應的論文。 不管你有沒有剁過手,你對看了這個商品的還看了這樣的推薦形式一定不陌生。無論是貓還是狗,或者是其他電商網站,這樣的推薦產品可以說是推薦系統的標配了。 類似的還有,如點評標記類網站的喜歡了這部電影的還喜歡了,社交媒...
閱讀 3428·2021-10-20 13:49
閱讀 2803·2021-09-29 09:34
閱讀 3700·2021-09-01 11:29
閱讀 3087·2019-08-30 11:01
閱讀 844·2019-08-29 17:10
閱讀 883·2019-08-29 12:48
閱讀 2786·2019-08-29 12:40
閱讀 1358·2019-08-29 12:30