SORU
13 Ocak 2010, ÇARŞAMBA


Nasıl tamsayılar bir dizi her basamak sayısı için?

Bu metalik rakam evler, dolap kapakları, otel odaları, vb için kullanılan satmak hayal. Müşteri/ev kapıları numarası gerektiğinde gemi nasıl bulmak gerekir:

  • 100'de 1'dir
  • 300 51
  • Solda sıfır ile 1 2.000

Bariz çözüm için bir döngü önce son numarası, convert karşı bir dize ile ya da olmadan sıfır sol, özü, her bir basamak için bir dizin için bir artış dizisinin 10 tamsayılar.

Eğer bu, bütün tamsayılar aralığı döngüye gerek kalmadan çözmek için daha iyi bir yolu olup olmadığını merak ediyorum.

Herhangi bir dil veya yalancı çözümler bekliyoruz.


Düzenleme:

Cevapları gözden geçirin
John CashCommonsveWayne Conradbenim şimdiki yaklaşım iyi ve yeterince hızlı olduğunu yorum. Bakayım saçma bir benzetme: Eğer verilen görev sayarak meydanlarda bir satranç tahtasında en az 1 dakika olabilir son görev sayarak kareler tek tek amadaha iyiçözüm, daha sonra bir binanın taşları saymak için istenebilir çünkü taraf saymak ve bir çarpma yapmak.
Alex Reisnerbu sorun için, ne yazık ki, ilgili görünmüyor çok ilginç bir matematik Kanununa işaret ediyor.
Andresanlaşılacağı kullanıyorum aynı algoritma, ama dizeleri yerine işlemleri ile basamak ayıklamak.
John CashCommonsvephordönceden hesaplamak basamak gerekli ve bir arama tablosu bunları saklamak veya çiğ hız için bir dizi teklif. Bu ise mutlak bir taşınamaz, taş, en büyük tamsayı değeri ayarlamak iyi bir çözüm olabilir. Bunlardan birini hiç görmemiştim.
Yüksek Performanslı Markvesüzgeççeşitli aralıklar için gerekli basamak hesaplanmış. Bir millon sonucu bir orantı var işaret gibi görünüyor, ama diğer sayı için sonuçları farklı oranlarda gösterir.
süzgeçbulunan bazı formüller on bir güç olan numarasının sayı saymak için kullanılan olabilir. Robert Harveyçok ilginç bir deneyim MathOverflow de soru gönderme vardı. Matematik adamlardan biri bir çözüm matematiksel gösterim kullanarak yazdı.
Aaronaughtgelişmiş ve çözüm matematik kullanarak test etti. Bunu yazdıktan sonra formülleri Matematik Taşma kökenli ve bir kusur (nokta Stackoverflow için:) buldu yapılmıştır.
noahlavinebir algoritma geliştirdi ve yalancı olarak sundu.

Yeni bir çözüm
Tüm cevapları okuma ve bazı deneyler yaptıktan sonra, 10 ile 1 arasında bir tamsayı bir dizi buldumn-1:

  • Basamak 9, n 1*10(n-1)parçalara ihtiyaç vardır
  • 0, değilse sıfırları kullanarak basamak için n 10*n-1- ((10n-1) / 9) ihtiyaç vardır
  • Eğer sıfırları kullanarak 0, basamak için n 10*n-1- n gerekmektedir

İlk formül bulundusüzgeç(ve muhtemelen Diğerleri) diğer deneme ve yanılma ile buldum, ve (ama diğer cevaplar dahil olabilir).

N örneğin, = 6, Aralık 999,999 1:

  • Basamak 9 1 6*10 ihtiyacımız var5Her biri = 600,000
  • Sıfırları olmadan 0, basamak, 6*10 ihtiyacımız var5– (106-1)/9 = 600,000 - = 111,111 488,889
  • Sıfır sıfır 0, basamak, 6*10 ihtiyacımız var5– 6 = 599,994

Bu rakamlar kullanılarak kontrol edilebilirYüksek Performanslı Marksonuçları.

Bu formülleri kullanarak, orijinal algoritma geliştirdim. Hala döngüler ilk ve son sayı arasında tamsayılar, ama, eğer bulursa bir numara olan bir güç on, kullandığı formüller eklemek basamak sayısı miktarı için bir dizi 1 9 1 99 1 999 vb. İşte yalancı: algoritma

integer First,Last //First and last number in the range
integer Number     //Current number in the loop
integer Power      //Power is the n in 10^n in the formulas
integer Nines      //Nines is the resut of 10^n - 1, 10^5 - 1 = 99999
integer Prefix     //First digits in a number. For 14,200, prefix is 142
array 0..9  Digits //Will hold the count for all the digits

FOR Number = First TO Last
  CALL TallyDigitsForOneNumber WITH Number,1  //Tally the count of each digit 
                                              //in the number, increment by 1
  //Start of optimization. Comments are for Number = 1,000 and Last = 8,000.
  Power = Zeros at the end of number //For 1,000, Power = 3
  IF Power > 0                       //The number ends in 0 00 000 etc 
    Nines = 10^Power-1                 //Nines = 10^3 - 1 = 1000 - 1 = 999
    IF Number Nines <= Last            //If 1,000 999 < 8,000, add a full set
      Digits[0-9]  = Power*10^(Power-1)  //Add 3*10^(3-1) = 300 to digits 0 to 9
      Digits[0]   -= -Power              //Adjust digit 0 (leading zeros formula)
      Prefix = First digits of Number    //For 1000, prefix is 1
      CALL TallyDigitsForOneNumber WITH Prefix,Nines //Tally the count of each 
                                                     //digit in prefix,
                                                     //increment by 999
      Number  = Nines                    //Increment the loop counter 999 cycles
    ENDIF
  ENDIF 
  //End of optimization
ENDFOR  

SUBROUTINE TallyDigitsForOneNumber PARAMS Number,Count
  REPEAT
    Digits [ Number % 10 ]  = Count
    Number = Number / 10
  UNTIL Number = 0

Örneğin, aralık için 3,021, tezgaha 786 artar:

  • 790 786 (5 döngü) tarafından 1
  • 799 (1 döngü) 790-9
  • 800 799 1
  • 899 800 ila 99
  • 900 899 1
  • 999 900 99
  • 1000 999 1 tarafından
  • 1999 1000 999
  • 2000-1999) 1
  • 2999 için 2000'den 999
  • 3000 2999 1
  • 3010 3000 (10 döngü) tarafından 1
  • 3019 (1 döngü) 3010-9
  • 3021 için 3019 (2 kür) tarafından 1

Toplam: 28 döngüleri Optimizasyon olmadan: 2,235 döngüleri

Bu algoritma sıfırları olmadan sorunu çözer unutmayın. Öndeki ile kullanmak için bir hack kullandım

Eğer menzil sıfır sıfır 700 ila 1000 gerekiyorsa, 11,000 için 10,700 için algoritma kullanımı ve rakam sayısı 1 ile 1000 - 700 = 300 çıkarma o zaman.

Kriter ve Kaynak kodu

Özgün yaklaşım test ettim, aynı yaklaşımı kullanarak ve bazı büyük aralığı, bu sonuçlar ile yeni bir çözüm

Original             104.78 seconds
With               83.66
With Powers of Ten     0.07

Ekran görüntüsü kriter uygulama:
alt text

Eğer tam kaynak kodu görmek veya kriter çalıştırmak istiyorsanız bu Linkleri kullanın:

Kabul cevabı

noahlavineçözüm doğru olabilir, ama ben sadece sahte kod, bazı ayrıntıları veya tamamen izah eksik var sanırım takip edemedim.

Aaronaughtçözüm doğru gibi görünüyor, ama bu kod benim için çok karmaşık.

Ben de kabul ettimsüzgeçonun düşünce çizgisini beni bu yeni çözüm geliştirmek üzere yönlendirilirler. çünkü ’In cevabı, em

CEVAP
13 Ocak 2010, ÇARŞAMBA


Sayıları bir dizi halinde olduğu bir çözüm istediğinizi tahmin ediyorum, ve başlangıç ve bitiş numara. İşe yarayacağını başlangıç numarası ile başlangıç ve bitiş numarasını ulaşana kadar sayıp hayal et, ama yavaş olurdu. Bence hile için hızlı bir algoritma olduğu için bu fark için gidip bir basamak 10^x yerde tutun ve her şey aynı, kullanma rakam önce 10^x kat artı basamak 0-9 10^(x-1) kez. (Sayma taşıyan bir son x-th rakama dahil olabilir ama bunun için aşağı doğru.)

İşte size bir örnek. 1004 için 523 saymaya olduğunu söyle.

  • İlk, 523 524 için sizi Kont. Bu rakamı 5, 2, ve 4 kez her kullanır.
  • İkinci olarak, 604 için 524 say. En sağdaki basamak basamak boyunca 6 kez, her basamak 6 kopya. İkinci basamak basamak 0, 10 üzerinden 2'er kez geçer. Üçüncü rakam 6 5 kez ve 5 kez 100-24.
  • Üçüncü olarak, 1004 604 say. En sağdaki basamak eklemek her rakam 40 kopya yani 40 devir yapar. Doğru rakam, ikinci 4 zamanlı imtihan ekleyin her basamak 4 kopya. En soldaki rakam 100 her 7, 8, 6, 0, 9, artı 5 ve 100 - 5 yapar. Son rakamı 1 5 kat.

Son bit hızlandırmak için, en sağdaki iki yer ile ilgili kısma bak. Her basamak 10 1 kez kullanır. 1 10 ... 10^genel n = (10^(n 1) - 1) sayım daha da hızlandırmak için kullanabiliriz olan/9,.

Benim algoritma son sayı (taban-10 sayma kullanarak) Başlat sayı saymak, ama aslında bunu hızlı bir şekilde yapmak için yukarıdaki kullanmaktır. En azından en önemli ve bu rakam son olarak aynı sayı olacak şekilde yukarı doğru say her yerde itibaren bu sayının basamak arasında yineleme. Her noktada, n bir taşıyabilirsin önce-sayar yapmanız gereken, ve sayı m daha sonra yapmanız gereken sayısıdır.

Şimdi yalancı bir dil olarak kabul sayar. İşte o zaman ne yapardım

convert start and end numbers to digit arrays start[] and end[]
create an array counts[] with 10 elements which stores the number of copies of
     each digit that you need

iterate through start number from right to left. at the i-th digit,
    let d be the number of digits you must count up to get from this digit
        to the i-th digit in the ending number. (i.e. subtract the equivalent
        digits mod 10)
    add d * (10^i - 1)/9 to each entry in count.
    let m be the numerical value of all the digits to the right of this digit,
        n be 10^i - m.
    for each digit e from the left of the starting number up to and including the
        i-th digit, add n to the count for that digit.
    for j in 1 to d
        increment the i-th digit by one, including doing any carries
        for each digit e from the left of the starting number up to and including
            the i-th digit, add 10^i to the count for that digit
    for each digit e from the left of the starting number up to and including the
        i-th digit, add m to the count for that digit.
    set the i-th digit of the starting number to be the i-th digit of the ending
        number.

Ben değerini bir tarafından her seferinde 10^eski ben izlemek ve sadece 10 ile yeni bir tane almak için çarpın, yerine her zaman exponentiating artar madem.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • mist64

    mist64

    30 Mayıs 2006
  • TastyTuts | Creative video tutorials by Gareth David

    TastyTuts |

    6 Temmuz 2011
  • TomSka

    TomSka

    30 Mayıs 2006