查找整数c语言编程,关于算法:查找整数的位数
查找正整數的位數的最佳方法是什么?
我發現了這3種基本方法:
轉換為字符串
String s = new Integer(t).toString();
int len = s.length();
for循環
for(long long int temp = number; temp >= 1;)
{
temp/=10;
decimalPlaces++;
}
對數計算
digits = floor( log10( number ) ) + 1;
在大多數語言中,您都可以在其中計算log10(x)= ln(x)/ ln(10)。
首先,我認為字符串方法是最骯臟的方法,但是我想得越多,它就越是最快的方法。 還是?
首先定義"最佳"。然后選擇"最佳"算法很容易。
使用Luke來源:docjar.com/html/api/java/lang/Integer.java.html
這個問題可能屬于codegolf。
似乎這里沒有人對非基數10的整數沒有任何考慮。不使用Integer.toString(t, radix),temp = radix;(以及相應的numDigits++;,從十進制開始概括)或ln(x) ln(radix) ...
我無法想象使用對數會擊敗任何事物。沒有數據轉換,沒有循環,只是一個簡單,直接的計算。
@TMN:當您沒有log10函數或必須考慮精度損失的影響并證明您的公式將始終為給定范圍內的值返回正確的結果時,這并不容易。
您如何期望通過思考解決問題?您應該考慮測量。
您在很多方面都是正確的,我沒有定義"最佳"條件,因此我應該做比較冷杉(后來我做了作業,請參閱下文),但是此討論帶來了很多好主意;)
@Joey Adams:任何對數都可以。例如,log10(x)= ln(x)/ ln(10)。
@讓-弗朗索瓦·科貝特(Jean-Franois Corbett):的確,這在數學上是正確的。但是您是否100%確信它將與機器浮點算法一起正常工作?
@Joey Adams:好點……嗯,我的浮點數ln(e)有信心。
對于整數:floor(ln(n + 0.5) ln(10))可能有助于降低精度...
總有這種方法:
n = 1;
if ( i >= 100000000 ) { n += 8; i /= 100000000; }
if ( i >= 10000 ? ? ) { n += 4; i /= 10000; }
if ( i >= 100 ? ? ? ) { n += 2; i /= 100; }
if ( i >= 10 ? ? ? ?) { n += 1; }
如果您知道一些需要支持的整數范圍,那么這將非常有效。
@jimreed:對。您需要知道ceil(log2(log10(MAXINT)))。然后,它最多執行截斷分割的數量。
或者,如果這是一個真正的編碼問題,而不僅僅是難題,并且您知道整數始終小于10,000,那么您甚至不需要前兩個if語句。
正確的答案是對其進行度量-但是您應該能夠猜測轉換字符串并遍歷字符串以尋找結束標記所涉及的CPU步驟數
然后考慮您的處理器可以執行多少個FPU操作,以及計算單個日志有多容易。
編輯:在星期一早上浪費更多時間:-)
String s = new Integer(t).toString();
int len = s.length();
高級語言的問題之一是猜測一個看起來很簡單的語句在幕后系統正在做多少工作。強制Joel鏈接
該語句涉及為字符串以及可能為字符串的幾個臨時副本分配內存。它必須解析整數并將其數字復制到字符串中,如果數量很大,可能必須重新分配并移動現有內存。它可能必須檢查一堆區域設置來確定您的國家使用的是","還是"。",它可能必須進行一堆unicode轉換。
然后找到長度必須掃描整個字符串,再次考慮unicode和任何本地特定設置,例如-您是右語言還是左語言?
或者:
digits = floor( log10( number ) ) + 1;
僅僅因為這對您來說很難在紙上做并不意味著對計算機來說就很難了!實際上,高性能計算的一個好規則似乎是-如果某件事對人來說很困難(流體動力學,3d渲染),則對計算機來說很容易;如果某件事對人來說很容易(人臉識別,檢測語音)嘈雜的房間)很難用電腦!
您通常可以假定內置的數學函數log / sin / cos等-50年來一直是計算機設計的重要組成部分。因此,即使它們沒有直接映射到FPU的硬件功能中,您也可以打賭,替代實施非常有效。
@PMF…或ValueError或鼻惡魔。因此答案是floor(log10(number || 1)) + 1,或更明確地說是:(number == 0) ? 1 : (floor(log10(number)) + 1)
為什么不使用ceil()而不是floor() + 1?
@JosieThompson ceil(0)= 0,而floor(0)+1 = 1。
我在OCaml中創建了一個小腳本來測試不同的方法(字符串,遞歸,循環,日志),結果表明log是最快的。從最快到最慢的順序為(1)log,(2)string,(3)recursion,(4)loop。
我不知道,答案可能會有所不同,具體取決于您所使用的語言的實現方式。
所以,壓力測試吧!實施所有三個解決方案。以1到1,000,000(或代表解決方案的數字代表的其他一些巨大數字)上運行它們,以及每個模型花費的時間。
將您的解決方案相互對立,讓他們相互競爭。就像知識分子的角斗士。輸入三種算法!一種算法離開了!
假設以下條件,此算法也可能很好:
數字是整數和二進制編碼(<
我們不知道數字界限
var num = 123456789L;
var len = 0;
var tmp = 1L;
while(tmp < num)
{
len++;
tmp = (tmp << 3) + (tmp << 1);
}
該算法的速度應與提供的for循環(2)相當,但是由于(2個移位,加法和減法,而不是除法)而快一些。
對于Log10算法,由于用于計算Log函數的解析公式具有無限循環且無法精確計算Wiki,因此它只會給您近似的答案(接近真實值,但仍是)。
這里我們只需要對數的整數部分,這會使"無限循環"無效。
是的,您是對的,它使計算變得有限,但對于小數而言似乎仍然很昂貴。
不應該是:tmp += (tmp << 3) + (tmp << 1);嗎?
如果輸入數字可被10整除,這實際上無法正確計算。要解決此問題,如果數字可被10整除,則應將數字長度增加1。
如果有人想知道<
@mmstick不,不是被10整除的數字,該算法僅對10(包括1)和0的所有冪均無效。它給出20、30、40等的正確答案。
測試條件
十進制數字系統
正整數
最多10位數字
語言:ActionScript 3
結果
digits: [1,10],
no. of runs: 1,000,000
random sample: 8777509,40442298,477894,329950,513,91751410,313,3159,131309,2
result: 7,8,6,6,3,8,3,4,6,1
CONVERSION TO STRING: 724ms
LOGARITMIC CALCULATION: 349ms
DIV 10 ITERATION: 229ms
MANUAL CONDITIONING: 136ms
注意:作者不愿對超過10位數字的結論做出任何結論。
腳本
package {
import flash.display.MovieClip;
import flash.utils.getTimer;
/**
* @author Daniel
*/
public class Digits extends MovieClip {
private const NUMBERS : uint = 1000000;
private const DIGITS : uint = 10;
private var numbers : Array;
private var digits : Array;
public function Digits() {
// ************* NUMBERS *************
numbers = [];
for (var i : int = 0; i < NUMBERS; i++) {
var number : Number = Math.floor(Math.pow(10, Math.random()*DIGITS));
numbers.push(number);
}
trace('Max digits: ' + DIGITS + ', count of numbers: ' + NUMBERS);
trace('sample: ' + numbers.slice(0, 10));
// ************* CONVERSION TO STRING *************
digits = [];
var time : Number = getTimer();
for (var i : int = 0; i < numbers.length; i++) {
digits.push(String(numbers[i]).length);
}
trace('
CONVERSION TO STRING - time: ' + (getTimer() - time));
trace('sample: ' + digits.slice(0, 10));
// ************* LOGARITMIC CALCULATION *************
digits = [];
time = getTimer();
for (var i : int = 0; i < numbers.length; i++) {
digits.push(Math.floor( Math.log( numbers[i] ) / Math.log(10) ) + 1);
}
trace('
LOGARITMIC CALCULATION - time: ' + (getTimer() - time));
trace('sample: ' + digits.slice(0, 10));
// ************* DIV 10 ITERATION *************
digits = [];
time = getTimer();
var digit : uint = 0;
for (var i : int = 0; i < numbers.length; i++) {
digit = 0;
for(var temp : Number = numbers[i]; temp >= 1;)
{
temp/=10;
digit++;
}
digits.push(digit);
}
trace('
DIV 10 ITERATION - time: ' + (getTimer() - time));
trace('sample: ' + digits.slice(0, 10));
// ************* MANUAL CONDITIONING *************
digits = [];
time = getTimer();
var digit : uint;
for (var i : int = 0; i < numbers.length; i++) {
var number : Number = numbers[i];
if (number < 10) digit = 1;
else if (number < 100) digit = 2;
else if (number < 1000) digit = 3;
else if (number < 10000) digit = 4;
else if (number < 100000) digit = 5;
else if (number < 1000000) digit = 6;
else if (number < 10000000) digit = 7;
else if (number < 100000000) digit = 8;
else if (number < 1000000000) digit = 9;
else if (number < 10000000000) digit = 10;
digits.push(digit);
}
trace('
MANUAL CONDITIONING: ' + (getTimer() - time));
trace('sample: ' + digits.slice(0, 10));
}
}
}
感謝您運行所有這些測試;這非常有幫助!當然,確切的速度會因環境而異,但這是對預期結果以及如何執行此過程的很好概述。
您缺少通過使用移位循環而不是除法來計算數字的選項。
轉換為字符串:這將必須遍歷每個數字,找到映射到當前數字的字符,然后將一個字符添加到字符集合中。然后獲取生成的String對象的長度。將在O(n)中運行n =#個數字。
for循環:將執行2個數學運算:將數字除以10并增加一個計數器。將在O(n)中運行n =#個數字。
對數:將調用log10和floor并加1。看起來像O(1),但我不確定Log10或floor函數有多快。我對這類事情的了解因缺乏使用而萎縮,因此這些功能可能隱藏著復雜性。
因此,我想可以歸結為:查找數字映射的速度是否比多個數學運算或log10中發生的一切更快?答案可能會有所不同。在某些平臺上,字符映射更快,而在其他平臺上,計算速度更快。還要記住的是,第一個方法將創建一個新的String對象,該對象僅出于獲取長度的目的而存在。這可能會比其他兩種方法使用更多的內存,但這可能會或可能不會。
轉換為字符串也可能會在內部執行類似第二個循環的操作。
您顯然可以從競爭中刪除方法1,因為它使用的atoi / toString算法類似于方法2。
方法3的速度取決于是否為指令集包括日志基數10的系統編譯代碼。
在您使用的任何編程語言中使用最簡單的解決方案。我想不出在任何(有用的)程序中計數整數的位數都會成為瓶頸的情況。
C,C ++:
char buffer[32];
int length = sprintf(buffer,"%ld", (long)123456789);
Haskell:
len = (length . show) 123456789
JavaScript:
length = String(123456789).length;
PHP:
$length = strlen(123456789);
Visual Basic(未經測試):
length = Len(str(123456789)) - 1
如果知道位數并且相對較低,那么我只能同意你的看法。
R:nchar(123456789)
對于非常大的整數,log方法要快得多。例如,使用2491327數字(如果需要的話,第11920928個斐波那契數字),Python花費幾分鐘執行10分頻算法,并花費毫秒執行1+floor(log(n,10))。
import math
def numdigits(n):
return ( int(math.floor(math.log10(n))) + 1 )
您的意思是int(math.floor(math.log10(n or 1))) + 1,因為log10(0)會提高ValueError,而ceil(log10(1)), ceil(log10(10)), ...是0, 1, ...,而floor(...) + 1是正確的值。
它不適用于999999999999999(15位數字),返回16
關于您提議的"確定在給定基數中表示給定數字所必需的數字位數"的三種方法,我實際上并不喜歡它們中的任何一種。我更喜歡下面提供的方法。
重新使用方法1(字符串):任何涉及在字符串和數字之間來回轉換的操作通常都很慢。
重新執行方法2(temp / = 10):這是致命的錯誤,因為它假定x / 10始終表示" x除以10"。但是在許多編程語言(例如C,C ++)中,如果" x"是整數類型,則" x / 10"表示"整數除法",這與浮點除法并不相同,因此它引入了在每次迭代中都舍入錯誤,并且它們以遞歸公式累積,例如您的解決方案2使用的公式。
重新使用方法3(日志):對于大量數字(至少在C語言以及其他語言中也是如此)來說,這是有問題的,因為浮點數據類型往往不如64位整數那么精確。
因此,我不喜歡這3種方法:#1有效,但速度慢,#2損壞,#3對于大號車有故障。相反,我更喜歡這種方法,它適用于從0到大約18.44億五千萬的數字:
unsigned NumberOfDigits (uint64_t Number, unsigned Base)
{
unsigned Digits = 1;
uint64_t Power ?= 1;
while ( Number / Power >= ?Base )
{
++Digits;
Power *= Base;
}
return Digits;
}
把事情簡單化:
long long int a = 223452355415634664;
int x;
for (x = 1; a >= 10; x++)
{
a = a / 10;
}
printf("%d", x);
這是Swift 4中的度量。
算法代碼:
extension Int {
var numberOfDigits0: Int {
var currentNumber = self
var n = 1
if (currentNumber >= 100000000) {
n += 8
currentNumber /= 100000000
}
if (currentNumber >= 10000) {
n += 4
currentNumber /= 10000
}
if (currentNumber >= 100) {
n += 2
currentNumber /= 100
}
if (currentNumber >= 10) {
n += 1
}
return n
}
var numberOfDigits1: Int {
return String(self).count
}
var numberOfDigits2: Int {
var n = 1
var currentNumber = self
while currentNumber > 9 {
n += 1
currentNumber /= 10
}
return n
}
}
測量代碼:
var timeInterval0 = Date()
for i in 0...10000 {
i.numberOfDigits0
}
print("timeInterval0: \(Date().timeIntervalSince(timeInterval0))")
var timeInterval1 = Date()
for i in 0...10000 {
i.numberOfDigits1
}
print("timeInterval1: \(Date().timeIntervalSince(timeInterval1))")
var timeInterval2 = Date()
for i in 0...10000 {
i.numberOfDigits2
}
print("timeInterval2: \(Date().timeIntervalSince(timeInterval2))")
Output
timeInterval0: 1.92149806022644
timeInterval1: 0.557608008384705
timeInterval2: 2.83262193202972
在此基礎上,字符串轉換是Swift語言的最佳選擇。
您可以使用遞歸解決方案而不是循環,但是在某種程度上類似:
@tailrec
def digits (i: Long, carry: Int=1) : Int = ?if (i < 10) carry else digits (i/10, carry+1)
digits (8345012978643L)
對于長整數,圖片可能會發生變化-根據不同的算法分別測量較小和較長的數字,然后選擇合適的數字。 :)
當然,沒有什么能比開關更好的了:
switch (x) {
case 0: ?case 1: ?case 2: ?case 3: ?case 4: ?case 5: ?case 6: ?case 7: ?case 8: ?case 9: return 1;
case 10: case 11: // ...
case 99: return 2;
case 100: // you get the point :)
default: return 10; // switch only over int
}
除了普通數組:
int [] size = {1,1,1,1,1,1,1,1,1,2,2,2,2,2,... };
int x = 234561798;
return size [x];
有人會告訴您優化代碼大小,但是要知道,過早的優化...
let numDigits num =
let num = abs(num)
let rec numDigitsInner num =
match num with
| num when num < 10 -> 1
| _ -> 1 + numDigitsInner (num / 10)
numDigitsInner num
F#版本,不強制轉換為字符串。
看到@ daniel.sedlacek結果后,我感到很好奇,因此我使用Swift對超過10位數字進行了測試。我在操場上運行了以下腳本。
let base = [Double(100090000000), Double(100050000), Double(100050000), Double(100000200)]
var rar = [Double]()
for i in 1...10 {
for d in base {
let v = d*Double(arc4random_uniform(UInt32(1000000000)))
rar.append(v*Double(arc4random_uniform(UInt32(1000000000))))
rar.append(Double(1)*pow(1,Double(i)))
}
}
print(rar)
var timeInterval = NSDate().timeIntervalSince1970
for d in rar {
floor(log10(d))
}
var newTimeInterval = NSDate().timeIntervalSince1970
print(newTimeInterval-timeInterval)
timeInterval = NSDate().timeIntervalSince1970
for d in rar {
var c = d
while c > 10 {
c = c/10
}
}
newTimeInterval = NSDate().timeIntervalSince1970
print(newTimeInterval-timeInterval)
Results of 80 elements
0.105069875717163 for floor(log10(x))
0.867973804473877 for div 10 iterations
log(x,n)-mod(log(x,n),1)+1
其中x是基數,n是數字。
我在實驗" Math.floor(Math.log(numbers [i])/ Math.log(10))+ 1"中使用了此公式,并且有很多更快的方法。
總結
以上是生活随笔為你收集整理的查找整数c语言编程,关于算法:查找整数的位数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java canvas 画图片_canv
- 下一篇: Jest 只MOCK模块中的某个功能实现