Verilen dört milyar arasında olmayan bir tamsayı oluşturun


692

Bu röportaj sorusu bana verildi:

Dört milyar tamsayı içeren bir girdi dosyası verildiğinde, dosyada bulunmayan bir tamsayı oluşturmak için bir algoritma sağlayın. 1 GB belleğiniz olduğunu varsayın. Yalnızca 10 MB belleğiniz varsa ne yapacağınızı takip edin.

Analizim:

Dosyanın boyutu 4 × 10 9 × 4 bayt = 16 GB'dir.

Harici sıralama yapabiliriz, böylece tamsayıların aralığını bize bildiririz.

Benim sorum sıralanmış büyük tamsayı kümelerinde eksik tamsayıyı tespit etmenin en iyi yolu nedir?

Anlayışım (tüm cevapları okuduktan sonra):

32 bit tamsayılardan bahsettiğimizi varsayarsak, 2 32 = 4 * 10 9 vardır. farklı tamsayı vardır.

Durum 1: 1 GB = 1 * 10 9'umuz var * 8 bit = 8 milyar bit belleğimiz var.

Çözüm:

Farklı bir tamsayıyı temsil eden bir bit kullanırsak, yeterlidir. sıralamaya ihtiyacımız yok.

Uygulama:

int radix = 8;
byte[] bitfield = new byte[0xffffffff/radix];
void F() throws FileNotFoundException{
    Scanner in = new Scanner(new FileReader("a.txt"));
    while(in.hasNextInt()){
        int n = in.nextInt();
        bitfield[n/radix] |= (1 << (n%radix));
    }

    for(int i = 0; i< bitfield.lenght; i++){
        for(int j =0; j<radix; j++){
            if( (bitfield[i] & (1<<j)) == 0) System.out.print(i*radix+j);
        }
    }
}

Durum 2: 10 MB bellek = 10 * 10 6 * 8 bit = 80 milyon bit

Çözüm:

Tüm olası 16 bit önekler için, 2 16 sayı tamsayı = 65536, 2 16 * 4 * 8 = 2 milyon bite ihtiyacımız var. 65536 kova üretmemiz gerekiyor. Her kova için tüm olasılıkları elinde tutan 4 bayta ihtiyacımız var çünkü en kötü durum 4 milyar tam sayının hepsi aynı kovaya ait.

  1. Dosyadan ilk geçişte her bir bölümün sayacını oluşturun.
  2. Kovaları tarayın, 65536'dan daha az isabet alan ilk olanı bulun.
  3. Dosyanın ikinci geçişinden 2. adımda yüksek 16 bit önekleri bulunan yeni kovalar oluşturun
  4. 3. adımda inşa edilen kovaları tarayın, isabetsiz olan ilk kovayı bulun.

Kod yukarıdakine çok benzer.

Sonuç: Dosya geçişini artırarak hafızayı azaltıyoruz.


Geç gelenler için bir açıklama: Soru, sorulduğu gibi, dosyada bulunmayan tam olarak bir tamsayı olduğunu söylemez - en azından çoğu kişi bunu yorumlamaz. Yorum dizisindeki Birçok yorumlar vardır gerçi, görevin konusu varyasyonun hakkında. Ne yazık ki, yorum dizisine tanıtan yorum daha sonra yazarı tarafından silindi, bu yüzden artık yetim cevaplar her şeyi yanlış anladı gibi görünüyor. Çok kafa karıştırıcı, üzgünüm.


32
@trashgod, yanlış. 4294967295 benzersiz tamsayılar için 1 tamsayı kalır. Bunu bulmak için, tüm tamsayıları toplamalı ve olası tüm tamsayıların önceden hesaplanmış toplamından almalısınız.
Nakilon

58
Bu "Programlama İncileri" nin ikinci "incisi" dir ve kitaptaki tüm tartışmayı okumanızı öneririm. Bkz books.google.com/...
Alok Singhal

8
@Richard 64 bit int yeterince büyük olurdu.
cftarnas

79
int getMissingNumber(File inputFile) { return 4; }( referans )
johnny

14
C / C ++ HER ZAMAN gibi dillerde tamsayı türü ilişkilendirilebilirlik ve iletişim gibi özellikleri koruduğundan, 1 ile 2 ^ 32 arasındaki tüm tamsayıların toplamını depolayamamanız önemli değildir. Bunun anlamı, toplam doğru cevap olmamasına rağmen, taşma ile beklenen, taşma ile gerçek toplamı hesaplarsanız ve sonra çıkarırsanız, sonucun yine de doğru olacağıdır (taşmadığı sürece).
thedayturns

Yanıtlar:


530

"Tamsayı" nın 32 bit anlamına geldiğini varsayarsak, herhangi bir 16 bit önekle giriş dosyasında kaç sayı olduğunu saymak için 10 MB alan yeterlidir, bir geçişte tüm olası 16 bit önekler için giriş dosyası. Kovalardan en az biri 2 16 defadan az çarpmış olacak . Bu gruptaki olası numaralardan hangilerinin zaten kullanıldığını bulmak için ikinci bir geçiş yapın.

32 bitten fazla, ancak yine de sınırlı boyutta ise : Yukarıdaki gibi yapın, 32 bit aralığının (imzalı veya imzasız; seçiminiz) dışında kalan tüm giriş numaralarını yok sayarak.

"Tamsayı" matematiksel tamsayı anlamına gelirse : Girişi bir kez okuyun ve gördüğünüz en uzun sayının en büyük sayı uzunluğunu takip edin . İşiniz bittiğinde, en fazla artı bir rakama sahip rastgele bir sayının çıktısını alın . (Dosyadaki sayılardan biri tam olarak temsil etmek için 10 MB'tan fazla süren bir bignum olabilir, ancak giriş bir dosyaysa, en azından içine uyan herhangi bir şeyin uzunluğunu temsil edebilirsiniz ).


24
Mükemmel. İlk cevabınız dosyadan sadece 2 geçiş gerektirir!
corsiKa

47
10 MB bignum mu? Bu oldukça aşırı.
Mark Ransom

12
@Legate, sadece büyük numaraları atlayın ve onlar hakkında hiçbir şey yapmayın. Yine de büyük bir sayı çıkarmayacağınız için hangisini gördüğünüzü takip etmenize gerek yok.
hmakholm Monica

12
Çözüm 1 ile ilgili iyi olan şey, geçişleri artırarak belleği azaltabilmenizdir.
Yousf

11
@Barry: Yukarıdaki soru tam olarak bir numaranın eksik olduğunu göstermiyor. Dosyadaki numaraların da tekrarlanmadığını söylemez. (Aslında sorulan soruyu takiben bir röportajda muhtemelen iyi bir fikir, değil mi? ;-))
Christopher Creutzig

197

İstatistiksel olarak bilgilendirilmiş algoritmalar, bu sorunu deterministik yaklaşımlardan daha az geçiş kullanarak çözer.

Çok büyük tamsayılara izin verilirse , O (1) zamanında benzersiz olması muhtemel bir sayı üretilebilir. GUID gibi sözde rastgele bir 128 bit tam sayı , kümedeki mevcut dört milyar tam sayıdan biriyle, her 64 milyar milyar milyar vakadan birinden daha azında çarpışacaktır.

Tamsayılar 32 bit ile sınırlıysa, tek bir geçişte 10 MB'den daha az kullanan benzersiz bir sayı üretilebilir. Sözde rastgele 32 bitlik bir tamsayının mevcut 4 milyar tamsayıdan biri ile çarpışma olasılığı yaklaşık% 93'tür (4e9 / 2 ^ 32). 1000 sahte rasgele tamsayıların çarpışması ihtimali 12.000 milyar milyar milyardan birinden daha azdır (bir çarpışma olasılığı ^ 1000). Dolayısıyla, bir program 1000 sahte rasgele aday içeren bir veri yapısını koruyor ve bilinen tamsayılarla yineliyor ve adaylardan eşleşmeleri ortadan kaldırıyorsa, dosyada olmayan en az bir tamsayıyı bulmak kesin değildir.


32
Tam sayıların sınırlı olduğundan eminim. Değilse, o zaman acemi bir programcı bile algoritma "maksimum sayı bulmak için veri bir geçiş almak ve ona 1 ekleyin" düşünürdüm
Adrian Petrescu

12
Kelimenin tam anlamıyla rastgele bir çıktı tahmin etmek muhtemelen bir röportajda size çok fazla puan alamaz
Brian Gordon

6
@Adrian, çözümünüz açık görünüyor (ve benim için, kendi cevabımda kullandım) ama herkes için açık değil. Belirgin çözümleri tespit edip edemeyeceğinizi veya dokunduğunuz her şeyi aşırı karmaşıklaştıracağınızı görmek iyi bir testtir.
Mark Ransom

19
@Brian: Bence bu çözüm hem yaratıcı hem de pratik. Ben bu cevap için çok kudos veririm.
Richard H

6
ah burada mühendisler ve bilim adamları arasındaki çizgi yatıyor. Harika cevap Ben!
TrojanName

142

Bu sorunla ilgili ayrıntılı bir tartışma Jon Bentley "Sütun 1. İstiridye Kırma" Programlama İncileri Addison-Wesley s.3-10

Bentley, harici sıralama, çeşitli harici dosyaları kullanarak Birleştirme Sıralaması vb.Dahil olmak üzere çeşitli yaklaşımları tartışır. Ancak Bentley'in önerdiği en iyi yöntem, bit alanlarını kullanarak mizahi bir şekilde "Wonder Sort" olarak adlandırdığı tek bir geçiş algoritmasıdır :) Sorun geliyor, 4 milyar sayılar şu şekillerde temsil edilebilir:

4 billion bits = (4000000000 / 8) bytes = about 0.466 GB

Bitset'i uygulamak için kod basit: ( çözümler sayfasından alınmıştır )

#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];

void set(int i) {        a[i>>SHIFT] |=  (1<<(i & MASK)); }
void clr(int i) {        a[i>>SHIFT] &= ~(1<<(i & MASK)); }
int  test(int i){ return a[i>>SHIFT] &   (1<<(i & MASK)); }

Bentley algoritması dosya üzerinde tek bir geçiş yapar set, dizideki uygun biti bağlar ve ardından bu diziyitest eksik olan sayıyı bulmak için yukarıdaki makroyu .

Kullanılabilir bellek 0,466 GB'den azsa, Bentley girişi kullanılabilir belleğe bağlı olarak aralıklara ayıran bir k-pass algoritması önerir. Çok basit bir örnek vermek gerekirse, sadece 1 bayt (yani 8 rakamı işlemek için bellek) mevcutsa ve aralık 0 ila 31 arasındaysa, bunu 0 ila 7, 8-15, 16-22 aralığına böleriz vb. ve her 32/8 = 4geçişte bu aralığı idare edin .

HTH.


12
Ben kitap bilmiyorum, ama sadece bir bucketsort olduğu için "Wonder Sort" olarak adlandırmak için bir sebep yok, 1-bit sayacı ile.
flolo

3
Daha taşınabilir olsa da, bu kod donanım destekli vektör talimatlarını kullanmak için yazılan kodla imha edilecektir . Gcc bazı durumlarda olsa da vektör işlemleri kullanarak otomatik olarak kod dönüştürebilirsiniz düşünüyorum.
Brian Gordon

3
@brian Jon Bentley'nin algoritma üzerine kitabında böyle şeylere izin verdiğini sanmıyorum.
David Heffernan

8
@BrianGordon, ram'da harcanan zaman, dosyayı okumak için harcanan zamana kıyasla önemsiz olacaktır. Optimize etmeyi unutun.
Ian

1
@BrianGordon: Ya da ilk ayarlanmamış biti bulmak için sonunda döngüden mi bahsediyordunuz? Evet != -1. (En yeni Intel / AMD tasarımları için). Yalnızca, onu içeren 64 bit konumu bulduktan sonra hangi bitin ayarlanmadığını bulmanız gerekir. (Ve bunun için de yapabilirsiniz not / lzcnt.) Tek noktadan bir test üzerinde döngü yapmanın iyi bir şekilde optimize edilemeyeceği gerçeği.
Peter Cordes

120

Sorun, dosyada bulunmayan mümkün olan en küçük sayıyı bulmamız gerektiğini belirtmediğinden, yalnızca giriş dosyasının kendisinden daha uzun bir sayı üretebiliriz. :)


6
Dosyadaki en büyük sayı max int
değilse

Yeni bir tamsayı oluşturmak ve "kullanılmış tamsayılar" dosyasına 100 kez eklemek zorunda olabilecek gerçek bir Dünya programındaki dosyanın boyutu ne olabilir?
Michael

2
Bunu düşünüyordum. Varsayarsak intolan 32bit, sadece çıkış 2^64-1. Bitti.
imallett

1
Satır başına bir int ise tr -d '\n' < nums.txt > new_num.txt:: D
Shon

56

1 GB RAM varyantı için bir bit vektörü kullanabilirsiniz. 4 milyar bit == 500 MB bayt dizisi ayırmanız gerekir. Girişten okuduğunuz her sayı için ilgili biti '1' olarak ayarlayın. İşiniz bittiğinde, bitlerin üzerinde tekrarlayın, hala '0' olan ilk tanesini bulun. Endeks cevabı.


4
Girişteki sayı aralığı belirtilmedi. Girdi 8 milyar ila 16 milyar arasındaki tüm çift sayılardan oluşuyorsa bu algoritma nasıl çalışır?
Mark Ransom

27
@ Mark, 0..2 ^ 32 aralığının dışındaki girişleri yok saymanız yeterlidir. Zaten bunlardan hiçbirini üretmeyeceksiniz, bu yüzden hangilerinden kaçınacağınızı hatırlamanıza gerek yok.
hmakholm Monica

@ 32 bit dizenin gerçek bir sayıya nasıl eşlendiğini belirlemek için kullandığınız algoritmayı işaretleyin. Süreç hala aynı. Tek fark, ekrana gerçek bir sayı olarak nasıl yazdırdığınızdır.
corsiKa

4
Kendinizi yinelemek yerine şunları kullanabilirsiniz bitSet.nextClearBit(0): download.oracle.com/javase/6/docs/api/java/util/…
starblue

3
Tamsayıların aralığına bakılmaksızın, geçişin sonunda en az bir bitin 0 olduğunu garanti etmek yararlı olacaktır. Bunun nedeni güvercin deliği prensibidir.
Rafał Dowgird

46

32 bit tamsayılarsa (2 32'ye yakın ~ 4 milyar sayı arasından seçim yapılabilir ), 4 milyar sayı listeniz olası tam sayıların en fazla% 93'ünü kaplar (4 * 10 9 / (2 32 ) ). Bu nedenle , her bit sıfıra başlatılmış olarak 2 32 bitlik bir bit dizisi oluşturursanız (2 29 bayt ~ 500 MB RAM alır; bir bayt = 2 3 bit = 8 bit) hatırlayın , tam sayı listenizi okuyun ve her int için karşılık gelen bit dizisi elemanını 0'dan 1'e ayarlayın; ve bit dizinizi okuyun ve hala 0 olan ilk biti döndürün.

Daha az RAM'iniz varsa (~ 10 MB), bu çözümün biraz değiştirilmesi gerekir. 10 MB ~ 83886080 bit, 0 ile 83886079 arasındaki tüm sayılar için bir bit dizisi yapmak için hala yeterlidir. Böylece, ints listenizi okuyabilirsiniz; ve yalnızca bit dizinize 0 ile 83886079 arasındaki # sayılarını kaydedin. Sayılar rastgele dağıtılırsa; (yaklaşık 100 farkının% olasılığının ezici ile 10 -2592069 ) siz) Eksik int bulacaksınız. Aslında, yalnızca 1 ila 2048 arasındaki sayıları seçerseniz (yalnızca 256 bayt RAM ile), zamanın çok büyük bir yüzdesini (% 99.99999999999999999999999999999999999999999999999999999999999995) bulabilirsiniz.

Ama diyelim ki yaklaşık 4 milyar rakam yerine; 2 32 - 1 sayı ve 10 MB'den daha az RAM'iniz vardı; bu yüzden küçük bir dizi int, sadece sayıyı içermeme olasılığı düşüktür.

Eğer listedeki her int eşsiz olduğunu garanti olsaydı, sayılarını toplamak ve bir # tam toplamı (½) eksik olan toplamını çıkarmak olabilir (2 32 ) (2 32 - 1) eksik int bulmak için = 9223372034707292160 . Ancak, bir int iki kez meydana geldiğinde bu yöntem başarısız olur.

Ancak, her zaman bölebilir ve fethedebilirsiniz. Saf bir yöntem, dizi boyunca okumak ve ilk yarıdaki (0 ila 2 31 -1) ve ikinci yarıdaki (2 31 , 2 32 ) sayıların sayısını saymak olacaktır . Ardından, daha az sayı içeren aralığı seçin ve bu aralığı yarıya bölmeyi tekrarlayın. ((2 31 , 2 32 ) içinde daha az iki sayı varsa, bir sonraki aramanız aralıktaki sayıları sayar (2 31 , 3 * 2 30 -1), (3 * 2 30 , 2 32 ). sıfır sayı aralığını bulana ve cevabınız siz oluncaya kadar yinelenir.O dizisi boyunca O (lg N) ~ 32 değeri okunmalıdır.

Bu yöntem verimsizdi. Her adımda sadece iki tamsayı kullanıyoruz (veya 4 bayt (32 bit) tamsayı ile yaklaşık 8 bayt RAM). Daha iyi bir yöntem sqrt (2 32 ) = 2 16 = 65536 kutuya bölmek , her biri bir depoda 65536 sayıya sahip olmaktır. Her bölmenin sayısını saklamak için 4 bayt gerekir, bu nedenle 2 18 bayt = 256 kB'ye ihtiyacınız vardır. Yani kutu 0 (0 ila 65535 = 2 16 -1), kutu 1 (2 16 = 65536 ila 2 * 2 16 -1 = 131071), kutu 2 (2 * 2 16 = 131072 ila 3 * 2 16 - 1 = 196.607). Python'da şöyle bir şey olurdu:

import numpy as np
nums_in_bin = np.zeros(65536, dtype=np.uint32)
for N in four_billion_int_array:
    nums_in_bin[N // 65536] += 1
for bin_num, bin_count in enumerate(nums_in_bin):
    if bin_count < 65536:
        break # we have found an incomplete bin with missing ints (bin_num)

~ 4 milyar tam sayı listesini okuyun; ve 2 16 bölmenin her birine kaç tane int düştüğünü sayın ve 65536 numarasının tümüne sahip olmayan bir eksik_bin bulun. Sonra tekrar 4 milyar tam sayı listesini okudunuz; ancak bu kez yalnızca tamsayılar bu aralıktayken fark edilir; onları bulduğunuzda biraz çevirme.

del nums_in_bin # allow gc to free old 256kB array
from bitarray import bitarray
my_bit_array = bitarray(65536) # 32 kB
my_bit_array.setall(0)
for N in four_billion_int_array:
    if N // 65536 == bin_num:
        my_bit_array[N % 65536] = 1
for i, bit in enumerate(my_bit_array):
    if not bit:
        print bin_num*65536 + i
        break

3
Harika bir cevap. Bu aslında işe yarar; ve garantili sonuçlar verdi.
Jonathan Dickinson

@dr jimbob, bir bölmede yalnızca bir sayı varsa ve bu tek sayının 65535 kopyası varsa ne olur? Öyleyse, kutu yine de 65536 sayar, ancak 65536 numaralarının tümü aynıdır.
Alcott

@Alcott - 2 ^ 32-1 (veya daha az) sayıya sahip olduğunuzu varsaydım, bu yüzden güvercin deliği prensibine göre daha ayrıntılı kontrol etmek için 65536'dan daha az sayıya sahip bir kutuya sahip olmanız garanti edilir. Sadece bir eksik tamsayı bulmaya çalışıyoruz, hepsini değil. 2 ^ 32 veya daha fazla numaranız varsa, eksik bir tamsayıyı garanti edemezsiniz ve bu yöntemi kullanamazsınız (veya başlangıçta eksik bir tamsayı vardır). En iyi bahsiniz o zaman kaba kuvvet olacaktır (örneğin, diziyi 32 kez okuyun; ilk 65536 #'ları ilk kez kontrol edin; ve bir cevap bulunduğunda durun).
dr jimbob

Akıllı üst 16 / alt 16 yöntemi daha önce Henning tarafından yayınlanmıştır: stackoverflow.com/a/7153822/224132 . Gerçi tam olarak bir üye eksik benzersiz bir tamsayılar kümesi için add-up fikrini sevdim.
Peter Cordes

3
@PeterCordes - Evet, Henning'in çözümü benimkinden önce, ama cevabımın hala yararlı olduğunu düşünüyorum (birkaç şeyi daha ayrıntılı olarak çalışmak). Bununla birlikte, Programming Pearls adlı kitabında Jon Bentley, yığın akışı olmadan önce bu sorun için çok geçişli bir seçenek önerdi (asma cevabına bakın) (ikimizden birinin bilinçli olarak oradan çaldığını veya Bentley'nin Bu sorunu analiz edin - geliştirmek oldukça doğal bir çözümdür). Sınırlama, dev bir bit dizisine sahip bir 1 geçiş çözümü için yeterli belleğe sahip olmadığınızda iki geçiş en doğal görünmektedir.
dr jimbob

37

Neden bu kadar karmaşık? Dosyada olmayan bir tam sayı mı istiyorsunuz?

Belirtilen kurallara göre, depolamanız gereken tek şey dosyada şu ana kadar karşılaştığınız en büyük tam sayıdır. Tüm dosya okunduktan sonra, 1'den büyük bir sayı döndürün.

Kurallara göre, tamsayı boyutu veya algoritma tarafından döndürülen sayı ile ilgili herhangi bir kısıtlama olmadığından, maxint'e veya başka bir şeye çarpma riski yoktur.


4
Maks. İnt dosyada tamamen mümkün değilse bu işe
yarar

13
Kurallar, 32bit veya 64bit veya başka bir şey olduğunu belirtmez, bu nedenle belirtilen kurallara göre, max int yoktur. Tamsayı bir bilgisayar terimi değil, pozitif veya negatif tam sayıları tanımlayan bir matematik terimidir.
Pete

Yeterince doğru, ancak 64 bitlik bir sayı olduğunu ya da birisinin sadece bu tür algoritmaları karıştırmak için max int numarasında gizlice girmeyeceğini varsayamazsınız.
PearsonArtPhoto

24
"Max int" kavramı, hiçbir programlama dili belirtilmemişse bağlamda geçerli değildir. örneğin Python'un uzun bir tamsayı tanımına bakın. Sınırsız. Çatı yok. Her zaman bir tane ekleyebilirsiniz. Bir tamsayı için izin verilen maksimum değere sahip bir dilde uygulandığını varsayıyorsunuz.
Pete

32

Bu, ikili aramanın bir varyantı kullanılarak çok az alanda çözülebilir.

  1. , Sayıların izin verilen aralığın ile başlayın 0için 4294967295.

  2. Orta noktayı hesaplayın.

  3. Kaç tane sayıya eşit, orta nokta değerinden daha küçük veya daha yüksek olduğunu sayarak dosyada dolaşın.

  4. Hiçbir sayı eşit değilse, işiniz bitmiştir. Orta nokta numarası cevaptır.

  5. Aksi takdirde, en az sayıya sahip aralığı seçin ve bu yeni aralıkla 2. adımdan itibaren tekrarlayın.

Bu, dosya boyunca 32'ye kadar doğrusal tarama gerektirir, ancak aralığı ve sayıları depolamak için yalnızca birkaç bayt bellek kullanır.

Bu, Henning'in çözümü ile aynıdır, ancak 16k yerine iki kutu kullanır.


2
Verilen parametreler için optimizasyona başlamadan önce bu ile başladım.
hmakholm Monica

@Henning: Harika. Uzay-zaman dengesini değiştirmenin kolay olduğu bir algoritmaya güzel bir örnek.
Hamar

@hammar, ama bir kereden fazla görünen sayılar varsa?
Alcott

@Alcott: daha sonra algoritma, serpme bölmesi yerine daha yoğun bölmeyi seçecektir, ancak güvercin deliği ilkesine göre, tamamen dolu bir bölmeyi seçemez. (İki sayıdan daha küçük olanı her zaman depo aralığından daha az olacaktır.)
Peter Cordes

27

EDIT Tamam, dosyadaki tamsayıların bazı statik dağılımları izlediğini varsaydığı için bu tam olarak düşünülmedi. Görünüşe göre buna gerek yok, ama o zaman bile bunu denemelisin:


4.3 milyar 32 32-bit tamsayı vardır. Dosyada nasıl dağıldıklarını bilmiyoruz, ancak en kötü durum Shannon entropisine en yüksek olanı: eşit bir dağılım. Bu durumda, dosyada herhangi bir tamsayı oluşma olasılığı

((2³²-1) / 2³²) ⁴ ⁰⁰⁰ ⁰⁰⁰ ⁰⁰⁰ ≈ .4

Shannon entropisi ne kadar düşükse, bu olasılık ortalamada o kadar yüksek olur, ancak bu en kötü durum için bile, rasgele tamsayılarla 5 tahminden sonra ortaya çıkan bir sayı bulma şansımız% 90'dır. Sadece bu tür numaraları bir sözde jeneratörle oluşturun, bir listede saklayın. Sonra int sonra int okuyun ve tüm tahminlerinizle karşılaştırın. Bir eşleşme olduğunda, bu liste girişini kaldırın. Tüm dosyadan geçtikten sonra, birden fazla tahmininiz kalma şansı vardır. Bunlardan herhangi birini kullanın. Hiçbir tahmin kalmamış nadir (en kötü durumda% 10) olayında, yeni bir rastgele tamsayı seti, belki de bu sefer daha fazla (% 10-> 99) alın.

Bellek tüketimi: birkaç düzine bayt, karmaşıklık: O (n), tepegöz: neclectable olarak çoğu zaman ints karşılaştırmak yerine kaçınılmaz sabit disk erişimlerinde harcanacaktır.


Gerçek kötü durum, biz ne zaman değil statik dağılımını varsayalım, her tamsayı maksimum meydana olmasıdır. bir kez, çünkü o zaman sadece 1 - 4000000000 / 2³² ≈ Tüm tamsayıların% 6'sı dosyada oluşmaz. Yani daha fazla tahminde bulunmanız gerekecek, ancak bu yine de incitici bellek miktarına mal olmayacak.


5
Başka birinin bunu düşündüğünü gördüğüme sevindim, ama neden aşağıda en aşağıda? Bu 1 geçişli bir algo… 2.5 MB tahminler için 10 MB yeterlidir ve% 93 ^ 2.5M ≈ 10 ^ -79000 gerçekten ikinci bir taramaya ihtiyaç duyulan ihmal edilebilir bir şanstır. İkili arama yükü nedeniyle, daha az tahmin kullanırsanız daha hızlı gider! Bu hem zaman hem de mekan için idealdir.
Potatoswatter

1
@Potatoswatter: iyi ikili arama bahsetti. Muhtemelen sadece 5 tahmin kullanırken ek yüke değmez, ancak kesinlikle 10 veya daha fazladır. 2 M tahminlerini bile yapabilirsiniz, ancak daha sonra arama için O (1) elde etmek için bir karma kümesinde saklamanız gerekir.
leftaroundabout

1
@Potatoswatter Ben Haley'nin eşdeğer yanıtı tepede
Brian Gordon

1
Bu yaklaşımı beğendim, ancak bellek tasarrufu sağlayan bir iyileştirme önerebilirim: Birinde kullanılabilir N dizinli depolama biti ve bazı sabit depolama alanları varsa, yapılandırılabilir geri dönüşümlü 32 bit karıştırma işlevi (permütasyon) tanımlayın, rastgele bir permütasyon seçin ve tümünü temizleyin dizinli bitler. Sonra dosyadan her sayıyı okuyun, karıştırın ve sonuç N'den küçükse, karşılık gelen biti ayarlayın. Dosyanın sonunda herhangi bir bit ayarlanmazsa, dizinindeki karıştırma işlevini tersine çevirin. 64KB bellekle, tek bir geçişte kullanılabilirlik açısından 512.000'den fazla sayı etkili bir şekilde test edilebilir.
supercat

2
Tabii ki, bu algoritma ile en kötü durum, sayıların kullandığınız aynı rastgele sayı üreteci tarafından oluşturulduğu durumdur. Durum böyle olmadığını garanti edebileceğinizi varsayarsak, en iyi taktikiniz, listenizi oluşturmak için doğrusal bir kongüratif rasgele sayı üreteci kullanmaktır, böylece sayı uzayını sahte bir şekilde geçirirsiniz. Bu, bir şekilde başarısız olursanız, çabalarınızı çoğaltmadan tüm ints aralığını kaplayana kadar (bir boşluk bulduktan sonra) sayı üretmeye devam edebileceğiniz anlamına gelir.
Dewi Morgan

25

[0, 2 ^ x - 1] aralığından eksik bir tamsayı varsa, hepsini bir arada xorulayın. Örneğin:

>>> 0 ^ 1 ^ 3
2
>>> 0 ^ 1 ^ 2 ^ 3 ^ 4 ^ 6 ^ 7
5

(Bunun soruyu tam olarak cevaplamadığını biliyorum , ancak çok benzer bir soru için iyi bir cevap.)


1
Evet, bir tamsayı eksik olduğunda işe yaradığını0 ^ 1 ^ 3 ^ 4 ^ 6 ^ 7 [ ] kanıtlamak kolaydır , ancak birden fazla eksik varsa sık sık başarısız olur. Örneğin , 0'dır. [ 2 ila x'th güç için 2 x ve xor b için a ^ b, tüm k <2 x'in xor değeri sıfırdır - k ^ ~ k = (2 ^ x) - K <2 ^ (x-1) için 1 ve j = k + 2 ** (x-2) olduğunda k ^ ~ k ^ j ^ ~ j = 0 - yani bir sayı dışında hepsinin xor değeri olan]
James Waldby - jwpat7 24:11

2
İrcmaxell'in cevabı hakkındaki bir yorumda bahsettiğim gibi: Sorun "bir sayı eksik" demiyor, dosyadaki 4 milyar sayıya dahil olmayan bir sayı bulmayı söylüyor. 32 bitlik tamsayıları varsayarsak, dosyada yaklaşık 300 milyon sayı eksik olabilir. Eksik sayı ile eşleşen sayıların xoru olasılığı sadece% 7'dir.
James Waldby - jwpat7

Soruyu ilk kez okuduğumda düşündüğüm cevap bu, ancak daha yakından incelendiğinde sorunun bundan daha belirsiz olduğunu düşünüyorum. FYI, düşündüğüm soru bu: stackoverflow.com/questions/35185/…
Lee Netherton

18

Bir değerin büyük bir kümenin parçası olup olmadığını kesinlikle çok verimli bir şekilde belirleyen olasılıklı bir Bloom Filtresi duyup duymadığınızı görmek isteyebilirler (ancak yalnızca yüksek olasılıkla setin bir üyesi olduğunu belirleyebilirler.)


4
Muhtemelen ayarlanan olası değerlerin% 90'ından fazlasıyla, Bloom Filtrenizin büyük olasılıkla birçok cevabın zaten kullandığı bit alanına dejenere olması gerekir. Aksi takdirde, işe yaramaz tamamen dolu bir bit dizisi ile sonuçlanacaksınız.
Christopher Creutzig

@Christopher Bloom filtreleri hakkındaki anlayışım% 100'e ulaşana kadar dolu bir bitarray elde etmemeniz
Paul

... aksi halde yanlış negatifler alırsınız.
Paul

@ Doldurulmuş bir bit dizisi, izin verilen yanlış pozitifler verir. Bu durumda, çiçeklenme filtresi büyük olasılıkla negatif olan çözeltinin yanlış bir pozitif döndürdüğü duruma dejenere olur.
ataylor

1
@Paul: Karma işlevlerinin sayısı giriş sayısıyla çarpıldığında alanınızın uzunluğu kadar büyük olduğunda doldurulmuş bir bitarray elde edebilirsiniz. Tabii ki, bu istisnai bir durum olurdu, ancak olasılık oldukça hızlı bir şekilde yükselecektir.
Christopher Creutzig

17

Orijinal sorudaki mevcut ifadeye dayanarak, en basit çözüm:

Dosyadaki maksimum değeri bulun, sonra 1 değerini ekleyin.


5
MAXINT dosyaya dahil edilirse ne olur?
Petr Peller

@Petr Peller: Bir BIGINT kütüphanesi esasen tamsayı boyutundaki sınırlamaları kaldıracaktır.
oosterwal

2
@oosterwal, bu cevaba izin verildiyse, dosyayı okumanıza bile gerek yok - olabildiğince büyük bir sayı yazdırın.
Nakilon

1
@oosterwal, rastgele büyük sayınız yazdırabileceğiniz en büyük sayı ise ve dosyadaysa, bu görev çözülemedi.
Nakilon

3
@Nakilon: +1 Puanınız alınır. Bu, kabaca dosyadaki toplam basamak sayısını bulmaya ve bu kadar basamak içeren bir sayı yazdırmaya eşdeğerdir.
oosterwal

14

A kullanın BitSet. Bayt başına 8'de bir BitSet içine paketlenmiş 4 milyar tamsayı (2 ^ 32 tamsayıya kadar varsayarak) 2 ^ 32/2 ^ 3 = 2 ^ 29 = yaklaşık 0,5 Gb'dir.

Biraz daha fazla ayrıntı eklemek için - her sayıyı okuduğunuzda, BitSet'te ilgili biti ayarlayın. Ardından, mevcut olmayan ilk sayıyı bulmak için BitSet'i geçin. Aslında, bunu tekrar tekrar rastgele bir sayı seçerek ve varsa test ederek etkili bir şekilde yapabilirsiniz.

Aslında BitSet.nextClearBit (0) size ayarlanmamış ilk biti söyleyecektir.

BitSet API'sine bakıldığında, yalnızca 0..MAX_INT'i desteklediği görülüyor, bu nedenle biri + 've sayıları için ve diğeri numaralar için olmak üzere 2 BitSet'e ihtiyacınız olabilir, ancak bellek gereksinimleri değişmez.


1
Ya da BitSet... kullanmak istemiyorsanız bir dizi bit deneyin. Aynı şeyi yapar;)
jcolebrand 23:11

12

Boyut sınırı yoksa, en hızlı yol dosyanın uzunluğunu almak ve dosyanın uzunluğunu + 1 rasgele basamak sayısını (veya yalnızca "11111 ...") oluşturmaktır. Avantajı: dosyayı okumanıza bile gerek yoktur ve bellek kullanımını neredeyse sıfıra indirebilirsiniz. Dezavantajı: Milyarlarca basamak basacaksınız.

Bununla birlikte, tek faktör bellek kullanımını en aza indiriyorsa ve başka bir şey önemli değilse, bu en uygun çözüm olacaktır. Hatta size "kuralların en kötüye kullanılması" ödülünü bile verebilir.


11

Sayı aralığının her zaman 2 ^ n (2'nin çift gücü) olacağını varsayarsak, dışlayıcı veya işe yarar (başka bir posterde gösterildiği gibi). Neden olduğu kadarıyla kanıtlayalım:

Teori

2^nBir öğesi eksik olan öğelere sahip 0 tabanlı tamsayılar aralığı göz önüne alındığında , eksik sayıyı elde etmek için bilinen değerleri bir araya getirerek bu eksik öğeyi bulabilirsiniz.

Kanıt

N = 2'ye bakalım. N = 2 için, 4 benzersiz tamsayıyı temsil edebiliriz: 0, 1, 2, 3. Biraz kalıpları vardır:

  • 0 - 00
  • 1 - 01
  • 2 - 10
  • 3-11

Şimdi, bakarsak, her bit tam olarak iki kez ayarlanır. Bu nedenle, çift sayıda ayarlandığı ve münhasır-veya sayıların 0 olduğu için, tek bir sayı eksikse, münhasır- veya münhasır veya eksik sayıya sahip olduğunda bir sayı verir. Bu nedenle, eksik sayı ve sonuçta ortaya çıkan münhasır veya ored sayı tamamen aynıdır. 2'yi kaldırırsak, ortaya çıkan x veya10 (veya 2) olur.

Şimdi n + 1'e bakalım. Let birbirlerinin biraz ayarlanır sayısını diyoruz n, xve her bit ayarlanır sayısını n+1 y. Değeri yeşit olacaktır, y = x * 2çünkü bit 0 olarak ayarlanmış xelemanlar ve n+1bit 1 olarak ayarlanmış xelemanlar vardır n+1. Ve 2xher zaman eşit n+1olacağından, her bit her zaman eşit sayıda ayarlanmış olacaktır.

Bu nedenle, n=2çalışır ve n+1çalışır beri , xor yöntemi tüm değerleri için çalışacaktır n>=2.

0 Tabanlı Aralıkların Algoritması

Bu oldukça basit. 2 * n bellek kullanır, bu nedenle <= 32, 2 32 bit tam sayı aralığı için çalışır (dosya tanımlayıcı tarafından tüketilen belleği yok sayar). Ve dosyanın tek bir geçişini yapar.

long supplied = 0;
long result = 0;
while (supplied = read_int_from_file()) {
    result = result ^ supplied;
}
return result;

Keyfi Tabanlı Aralıkların Algoritması

Bu algoritma, toplam aralık 2 ^ n'ye eşit olduğu sürece, herhangi bir başlangıç ​​numarasının herhangi bir bitiş numarasına kadar olan aralıklarda çalışacaktır ... Bu, temelde en az 0'a sahip olmak için aralığı yeniden temeller. Ancak 2 geçiş gerektirir dosya üzerinden (minimum kapmak için ilk, eksik int hesaplamak için ikinci).

long supplied = 0;
long result = 0;
long offset = INT_MAX;
while (supplied = read_int_from_file()) {
    if (supplied < offset) {
        offset = supplied;
    }
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
    result = result ^ (supplied - offset);
}
return result + offset;

Keyfi Aralıklar

Bu değiştirilmiş yöntemi bir dizi rasgele aralığa uygulayabiliriz, çünkü tüm aralıklar en az bir kez 2 ^ n'lik bir gücü geçecektir. Bu sadece tek bir eksik bit varsa işe yarar. Sıralanmamış bir dosyanın 2 geçişini alır, ancak her seferinde eksik olan tek sayıyı bulur:

long supplied = 0;
long result = 0;
long offset = INT_MAX;
long n = 0;
double temp;
while (supplied = read_int_from_file()) {
    if (supplied < offset) {
        offset = supplied;
    }
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
    n++;
    result = result ^ (supplied - offset);
}
// We need to increment n one value so that we take care of the missing 
// int value
n++
while (n == 1 || 0 != (n & (n - 1))) {
    result = result ^ (n++);
}
return result + offset;

Temel olarak, aralığı 0 civarında temel alır. Ardından, exclusive- veya öğesini hesaplarken eklenecek ayrılmamış değerlerin sayısını sayar. Ardından, eksik değere bakmak için sıralanmamış değerlerin sayısına 1 ekler (eksik olanı sayın). Daha sonra, n değeri 2 olana kadar her seferinde 1 artarak n değerini xoring etmeye devam edin. Sonuç daha sonra orijinal tabana geri döndürülür. Bitti.

İşte PHP test algoritması (bir dosya yerine bir dizi kullanarak, ama aynı kavram):

function find($array) {
    $offset = min($array);
    $n = 0;
    $result = 0;
    foreach ($array as $value) {
        $result = $result ^ ($value - $offset);
        $n++;
    }
    $n++; // This takes care of the missing value
    while ($n == 1 || 0 != ($n & ($n - 1))) {
        $result = $result ^ ($n++);
    }
    return $result + $offset;
}

Herhangi bir değer aralığına sahip bir dizide beslenen (negatifler dahil olmak üzere test ettim), bu aralığın içinde eksik olan, her seferinde doğru değeri buldu.

Başka bir yaklaşım

Harici sıralamayı kullanabildiğimiz için, neden sadece bir boşluk kontrol etmiyoruz? Dosyanın bu algoritmanın çalıştırılmasından önce sıralandığını varsayarsak:

long supplied = 0;
long last = read_int_from_file();
while (supplied = read_int_from_file()) {
    if (supplied != last + 1) {
        return last + 1;
    }
    last = supplied;
}
// The range is contiguous, so what do we do here?  Let's return last + 1:
return last + 1;

3
Sorun "bir sayı eksik" demiyor, dosyadaki 4 milyar sayıya dahil olmayan bir sayı bulmayı söylüyor. 32 bitlik tamsayıları varsayarsak, dosyada yaklaşık 300 milyon sayı eksik olabilir. Eksik sayı ile eşleşen sayıların xoru olasılığı sadece% 7'dir.
James Waldby - jwpat7

Sıfır tabanlı olmayan bitişik ancak eksik bir aralığınız varsa, xor yerine ekleyin. sum(0..n) = n*(n+1)/2. Yani missing = nmax*(nmax+1)/2 - nmin*(nmin+1)/2 - sum(input[]). (@ hammar'ın cevabından gelen fikir toplamı)
Peter Cordes

9

Yanlış bir şekilde alıntılanmadığı sürece, hile sorusu. Maksimum tamsayıyı almak için dosyayı bir kez okuyun nve geri dönün n+1.

n+1Bir tamsayı taşmasına neden olması durumunda elbette bir yedekleme planına ihtiyacınız olacaktır .


3
İşte işe yarayan bir çözüm ... çalışmadığı sürece. Kullanışlı! :-)
dty

Yanlış bir şekilde belirtilmedikçe, soru tamsayı türüne, hatta kullanılan dile bağlı değildi. Birçok modern dilde yalnızca kullanılabilir bellekle sınırlanmış tamsayılar vardır. Dosyadaki en büyük tamsayı> 10MB ise, zor şans, ikinci durum için görev imkansızdır. En sevdiğim çözüm.
Jürgen Strobel

9

Giriş dosyasının boyutunu kontrol edin, ardından bu boyutta bir dosyayla temsil edilemeyecek kadar büyük herhangi bir sayı çıktısı alın . Bu ucuz bir numara gibi görünebilir, ancak bir röportaj problemine yaratıcı bir çözümdür, hafıza sorununu düzgün bir şekilde azaltır ve teknik olarak O (n).

void maxNum(ulong filesize)
{
    ulong bitcount = filesize * 8; //number of bits in file

    for (ulong i = 0; i < bitcount; i++)
    {
        Console.Write(9);
    }
}

Her zaman 2 bit sayımdan daha büyük olacak olan 10 bit - 1 yazdırmalıdır . Teknik olarak, dövmeniz gereken sayı 2 bit sayılır - (4 * 10 9 - 1) , çünkü dosyada (4 milyar - 1) başka tamsayı olduğunu biliyorsunuz ve mükemmel sıkıştırma ile bile en azından alacaklar her biri bir bit.


Neden sadece Console.Write( 1 << bitcount )döngü yerine değil ? Dosyada n bit varsa , önde 1 olan herhangi bir (_n_ + 1) bitlik sayının kesinlikle daha büyük olacağı garanti edilir.
Emmet

@Emmet - Dosya int boyutundan (C # 4 bayt) küçük değilse, tamsayı taşmasına neden olur. C ++ daha büyük bir şey kullanmanıza izin verebilir, ancak C #, işleçle 32 bit ints dışında bir şeye izin vermiyor gibi görünüyor <<. Her iki durumda da, kendi devasa tamsayı türünüzü döndürmezseniz, çok küçük bir dosya boyutu olacaktır. Demo: rextester.com/BLETJ59067
Justin Morgan

8
  • En basit yaklaşım, dosyadaki minimum sayıyı bulmak ve bundan daha az 1 döndürmektir. Bu, n numaralı bir dosya için O (1) depolama alanını ve O (n) süresini kullanır. Bununla birlikte, sayı aralığı sınırlıysa başarısız olur, bu da min-1'i bir sayı yapmaz.

  • Bir bitmap kullanmanın basit ve anlaşılır yönteminden daha önce bahsedilmiştir. Bu yöntem O (n) zaman ve depolamayı kullanır.

  • 2 ^ 16 sayma kabına sahip 2 geçişli bir yöntemden de bahsedilmiştir. 2 * n tamsayı okur, bu nedenle O (n) zaman ve O (1) depolamayı kullanır, ancak 2 ^ 16'dan fazla sayıya sahip veri kümelerini işleyemez. Bununla birlikte, 2 yerine 4 geçiş çalıştırarak (ör.) 2 ^ 60 64 bit tamsayılara kolayca genişletilebilir ve yalnızca belleğe sığacak kadar çok bölme kullanarak ve buna göre geçiş sayısını artırarak küçük bellek kullanımına kolayca uyarlanabilir. bu durumda çalışma süresi artık O (n) değil, O (n * log n) 'dir.

  • Tüm sayıları birlikte XOR'lama yöntemi, şimdiye kadar rfrankel ve uzunluk ircmaxell tarafından belirtildi , ltn100'ün belirttiği gibi stackoverflow # 35185'te sorulan soruya cevap veriyor . O (1) depolama ve O (n) çalışma süresi kullanır. Şimdilik 32 bit tamsayı varsayarsak, XOR% 7 oranında farklı bir sayı üretme olasılığına sahiptir. Gerekçe: XOR'lu ~ 4G farklı sayılar birlikte verilir ve ca. 300M dosyada değil, her bit konumundaki set bitlerinin sayısı tek veya çift olma şansına sahiptir. Bu nedenle, 2 ^ 32 sayısının XOR sonucu olarak ortaya çıkma olasılığı eşittir, bunun% 93'ü zaten dosyadadır. Dosyadaki sayıların hepsi farklı değilse, XOR yönteminin başarı olasılığının arttığını unutmayın.


7

Bazı nedenlerden dolayı, bu sorunu okuduğumda köşegenleştirmeyi düşündüm. Ben keyfi olarak büyük tamsayıları varsayıyorum.

İlk sayıyı okuyun. 4 milyar bitiniz olana kadar sıfır bitle sol tuşla doldurun. İlk (yüksek dereceli) bit 0 ise, çıkış 1; başka çıkış 0. (Gerçekten sol-pad yapmak zorunda değilsiniz: sayıda yeterli bit yoksa sadece 1 verirsiniz.) İkinci biti kullanmak dışında, ikinci sayı için de aynısını yapın. Dosya üzerinde bu şekilde devam edin. Bir kerede 4 milyar bitlik bir sayı çıktısı verirsiniz ve bu sayı dosyadakiyle aynı olmaz. İspat: n'inci sayı ile aynıydı, o zaman n'inci bit üzerinde hemfikir olacaklardı, ama inşaatla değiller.


Yaratıcılık için +1 (ve tek geçişli bir çözüm için şimdiye kadarki en kötü durum çıktısı).
hmakholm Monica

Ancak köşegenleştirilecek 4 milyar bit yok, sadece 32 tane var. Listedeki ilk 32 sayıdan farklı olan 32 bitlik bir sayı elde edeceksiniz.
Brian Gordon

@Henning Bu neredeyse tek bir geçiş değil; hala unary'den binary'ye geçmelisiniz. Edit: Sanırım bu dosya üzerinden bir geçiş. Boşver.
Brian Gordon

@Brian, burada "sıradan" bir şey var mı? Cevap, bir seferde bir bit ikili yanıt oluşturuyor ve giriş dosyasını yalnızca bir kez okur ve tek geçiş yapar. ( Ondalık çıktı gerekiyorsa, işler sorunludur - o zaman muhtemelen her üç giriş numarası için bir ondalık basamak oluşturmaktan daha iyidir ve çıktı numarasının günlüğünde% 10'luk bir artışı kabul edersiniz).
hmakholm Monica

2
@Henning Sorun keyfi olarak büyük tamsayılar için anlamsızdır, çünkü birçok insanın işaret ettiği gibi, sadece en büyük sayıyı bulmak ve bir tane eklemek veya dosyanın kendisinden çok uzun bir sayı oluşturmak önemsizdir. Bu köşegenleştirme çözümü özellikle uygunsuzdur, çünkü ith bitinde dallanmak yerine sadece 4 bitlik 1 bit çıkarabilir ve sonuna fazladan 1 atabilirsiniz. Algoritmada keyfi olarak büyük tamsayılara sahip olmakla sorun değilim ama sorun 32-bit tamsayı eksik çıktı olduğunu düşünüyorum. Başka hiçbir şekilde mantıklı değil.
Brian Gordon

6

Bir tamsayı olup olmadığını işaretlemek için bit bayraklarını kullanabilirsiniz.

Dosyanın tamamını gezdikten sonra, sayının var olup olmadığını belirlemek için her bir biti tarayın.

Her tamsayının 32 bit olduğunu varsayarsak, bit işaretlemesi yapılırsa 1 GB RAM'e rahatça sığarlar.


0,5 Gb, baytı 4 bit olarak yeniden tanımlamadıysanız ;-)
dty

2
@ dty Bence "rahat" anlamına gelir, çünkü 1Gb çok yer olacak.
corsiKa

6

Boşluk ve sayısal olmayan karakterleri dosyadan ayırın ve 1 ekleyin. Dosyanız şimdi orijinal dosyada listelenmeyen tek bir sayı içeriyor.

Reddit'ten Carbonetc tarafından.


Sevdim! Aradığı cevap tam olarak olmasa da ...: D
Johann du Toit

6

Tamlık uğruna, işte büyük olasılıkla çalışması çok uzun sürecek, ancak çok az bellek kullanan çok basit bir çözüm.

Tüm olası tamsayılar gelen aralık olalım int_minetmek int_maxve bool isNotInFile(integer)dosyası (dosyadaki her tamsayı ile belirli tamsayı karşılaştırarak) belli bir tamsayı ve yanlış başka içermiyorsa gerçek döndüren bir işlev

for (integer i = int_min; i <= int_max; ++i)
{
    if (isNotInFile(i)) {
        return i;
    }
}

Soru tam olarak isNotInFilefonksiyon algoritmasıyla ilgiliydi . Lütfen cevaplamadan önce soruyu anladığınızdan emin olun.
Aleks G

2
hayır, soru "dosyada hangi tamsayı değil", "dosyada x tamsayı" değil. ikinci sorunun cevabını belirleme işlevi, örneğin, dosyadaki her tamsayıyı söz konusu tam sayı ile karşılaştırabilir ve bir eşleşmede true değerini döndürebilir.
deg

Bunun meşru bir cevap olduğunu düşünüyorum. G / Ç dışında yalnızca bir tamsayı ve bir bool bayrağı gerekir.
Brian Gordon

@Aleks G - Bunun neden yanlış olarak işaretlendiğini anlamıyorum. Hepimiz en yavaş algoritmanın :-) olduğunu kabul ediyoruz, ancak çalışıyor ve dosyayı okumak için sadece 4 bayt gerekiyor. Orijinal soru dosyayı şart koşmaz örneğin sadece bir kez okunabilir.
Simon Mourier

1
@Aleks G - Doğru. Bunu da söylemiştin demedim. Sadece IsNotInFile dosyasının dosyadaki bir döngü kullanılarak önemsiz bir şekilde uygulanabileceğini söylüyoruz: Açık; Eof Değilken {Read Integer; Integer = i; Else Devam Et;}. Yalnızca 4 bayt belleğe ihtiyaç duyar.
Simon Mourier

5

10 MB bellek kısıtlaması için:

  1. Sayıyı ikili gösterimine dönüştürün.
  2. Sol = 0 ve sağ = 1 olan bir ikili ağaç oluşturun.
  3. Her sayıyı ikili gösterimini kullanarak ağaca ekleyin.
  4. Bir numara önceden eklenmişse, yapraklar zaten oluşturulmuş olacaktır.

Tamamlandığında, istenen numarayı oluşturmak için daha önce oluşturulmamış bir yolu seçmeniz yeterlidir.

4 milyar sayı = 2 ^ 32, yani 10 MB yeterli olmayabilir.

DÜZENLE

İki uçlu yaprak oluşturulduysa ve ortak bir üst öğeye sahipse, bir optimizasyon mümkündür ve bu çözüm kaldırılabilir ve üst öğe bir çözüm değil olarak işaretlenebilir. Bu dalları keser ve bellek ihtiyacını azaltır.

DÜZENLEME II

Ağacı tamamen inşa etmeye gerek yoktur. Sadece sayılar benzerse derin dallar oluşturmanız gerekir. Dalları da kesersek, bu çözüm aslında işe yarayabilir.


6
... ve bu 10 MB'a nasıl sığacak?
hmakholm Monica

Nasıl olur: BTree'nin derinliğini 10MB'ye sığacak bir şeyle sınırlayın; bu, kümede sonuçların olacağı anlamına gelir {yanlış pozitif | pozitif} ve bunu tekrarlayabilir ve değerleri bulmak için başka teknikler kullanabilirsiniz.
Jonathan Dickinson

5

1 GB versiyonuna cevap vereceğim:

Soruda yeterli bilgi yok, bu yüzden önce bazı varsayımları belirteceğim:

Tam sayı 32 bit olup -2.147.483.648 ila 2.147.483.647 aralığındadır.

Sözde kod:

var bitArray = new bit[4294967296];  // 0.5 GB, initialized to all 0s.

foreach (var number in file) {
    bitArray[number + 2147483648] = 1;   // Shift all numbers so they start at 0.
}

for (var i = 0; i < 4294967296; i++) {
    if (bitArray[i] == 0) {
        return i - 2147483648;
    }
}

4

Yaratıcı cevaplar yaptığımız sürece, işte bir tane daha.

Giriş dosyasını sayısal olarak sıralamak için harici sıralama programını kullanın. Bu, sahip olabileceğiniz herhangi bir bellek miktarı için çalışır (gerekirse dosya depolama alanını kullanır). Sıralanan dosyayı okuyun ve eksik olan ilk sayının çıktısını alın.


3

Bit Eleme

Bir yol, bitleri ortadan kaldırmaktır, ancak bu aslında bir sonuç vermeyebilir (şansı yoktur). Psuedocode:

long val = 0xFFFFFFFFFFFFFFFF; // (all bits set)
foreach long fileVal in file
{
    val = val & ~fileVal;
    if (val == 0) error;
}

Bit Sayısı

Bit sayılarını takip edin; ve bir değer üretmek için bitleri en az miktarda kullanın. Yine bunun doğru bir değer üretme garantisi yoktur.

Menzil Mantığı

Sıralı aralıkların listesini (başlangıç ​​tarafından sıralanan) takip edin. Bir aralık yapı tarafından tanımlanır:

struct Range
{
  long Start, End; // Inclusive.
}
Range startRange = new Range { Start = 0x0, End = 0xFFFFFFFFFFFFFFFF };

Dosyadaki her bir değeri gözden geçirin ve geçerli aralıktan kaldırmayı deneyin. Bu yöntemin bellek garantisi yoktur, ancak oldukça iyi olması gerekir.


3

2 128 * 10 18 + 1 ((2 8 ) 16 * 10 18 + 1) - bugün için evrensel bir cevap olamaz mı? Bu, 16 EB dosyasında tutulamayan bir sayıyı temsil eder; bu, mevcut herhangi bir dosya sistemindeki maksimum dosya boyutudur.


Ve sonucu nasıl basarsınız? Dosyaya koyamazsınız ve ekrana yazdırmak birkaç milyar yıl alır. Günümüzün bilgisayarlarında gerçekleştirilecek bir çalışma süresi yoktur.
vsz

sonucu hiçbir yerde basmamız gerektiği asla söylenmez, sadece 'üret'. bu yüzden oluşturmakla ne demek istediğine bağlı. Neyse, cevabım gerçek bir algoritma çalışmaktan kaçınmak için sadece bir hile :)
Michael Sagalovich

3

Bunun çözülmüş bir sorun olduğunu düşünüyorum (yukarıya bakın), ancak akılda tutulması gereken ilginç bir yan durum var, çünkü sorulabilir:

Tam olarak 4.294.967.295 (2 ^ 32-1) 32 bitlik tamsayılar yoksa ve bu nedenle yalnızca bir tanesi eksikse, basit bir çözüm vardır.

Çalışan bir toplamı sıfır olarak başlatın ve dosyadaki her tam sayı için bu biti 32 bit taşma ile ekleyin (etkili bir şekilde runningtotal = (runningTotal + nextInteger)% 4294967296). Tamamlandığında, yine 32 bit taşma ile çalışan toplama 4294967296/2 ekleyin. Bunu 4294967296'dan çıkarın ve sonuç eksik tam sayıdır.

"Yalnızca bir eksik tamsayı" sorunu yalnızca bir çalışma ve yalnızca 64 bit RAM (veriler için ayrılan RAM, bir sonraki tamsayıda okumak için 32) ile çözülebilir.

Sonuç: Tamsayı sonucunun kaç biti olması gerektiğinden endişe etmiyorsak, daha genel spesifikasyonun eşleştirilmesi son derece basittir. Sadece bize verilen dosyada bulunamayacak kadar büyük bir tamsayı üretiyoruz. Yine, bu kesinlikle minimum RAM gerektirir. Sözde koduna bakın.

# Grab the file size
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
# Print a '2' for every bit of the file.
for (c=0; c<sz; c++) {
  for (b=0; b<4; b++) {
    print "2";
  }
}

@Nakilon ve TheDayTurns, orijinal soruya yapılan yorumlarda bunu belirtti
Brian Gordon

3

Ryan'ın temelde söylediği gibi, dosyayı sıralayın ve tamsayıların üzerine gidin ve bir değer atlandığında orada var :)

Downvoters'ta DÜZENLE : OP, dosyanın geçerli bir yöntem olması için sıralanabileceğini belirtti.


Önemli bir nokta, gittiğiniz gibi yapmanız gerektiğidir, bu şekilde sadece bir kez okumak zorundasınız. Fiziksel belleğe erişim yavaş.
Ryan Amos

@ryan dış sıralama çoğu durumda bir birleştirme sıralamasıdır, bu nedenle son birleştirme işleminde çek yapabilirsiniz :)
cırcır ucube

Veriler disk üzerindeyse, belleğe yüklenmesi gerekir. Bu, dosya sistemi tarafından otomatik olarak gerçekleşir. Bir sayı bulmamız gerekiyorsa (sorun aksini belirtmez), sıralı dosyayı bir kerede bir satır okumak en etkili yöntemdir. Çok az bellek kullanır ve her şeyden daha yavaş değildir - dosya okunmalıdır.
Tony Ennis

Yalnızca 1 GB belleğiniz olduğunda 4 milyar tam sayıyı nasıl sıralayacaksınız? Virtyual bellek kullanırsanız, bellek blokları fiziksel belleğe girip çıktıkça çok zaman alacaktır.
Klas Lindbäck

4
@klas birleştirme sıralama bunun için tasarlanmıştır
cırcır ucube

2

32 bitlik kısıtlamayı kabul etmiyorsanız, rastgele oluşturulmuş bir 64 bit sayı (veya kötümserseniz 128 bit) döndürmeniz yeterlidir. Çarpışma şansı1 in 2^64/(4*10^9) = 4611686018.4 (kabaca 4 milyarda 1). Çoğu zaman haklısın!

(Şaka ... biraz.)


Bu zaten önerildiğini görüyorum :) bu insanlar için upvotes
Peter Gibson

Doğum günü paradoksu, rastgele tahmininizin gerçekten geçerli bir cevap olup olmadığını görmek için dosyayı kontrol etmeden bu tür bir çözümü riske değmez. (Doğum günü paradoksu bu durumda geçerli değildir, ancak tekrar tekrar bu işlevi yeni benzersiz değerler üretmek için çağırmak doğum günü paradoksu durumu yaratır.)
Peter Cordes

@PeterCordes Rastgele oluşturulan 128 bit sayıları tam olarak UUID'lerin nasıl çalıştığıdır - Wikipedia UUID sayfasında
Peter Gibson

Varyant: Sette maksimum değeri bulun, 1 ekleyin.
Phil

Ben orijinal dizi (ek depolama yok) quicksort sonra dizi üzerinden yürüyüş ve ilk 'atlanan' tamsayı rapor. Bitti. Soruyu yanıtladı.
Seviye 42
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.