Champaign Çeşmesi Yapboz


30

Boş bardak su aşağıdaki sırayla düzenlenir:

görüntü tanımını buraya girin

Doldurulursa 1. bardağa sıvı dökülürse, ilave sıvı bardağa 2 ve 3 eşit miktarlarda akar. Cam 2 dolduğunda, ilave sıvı 4 ve 5'e vb.

Bir N litre sıvı verildiğinde ve her bir camın maksimum kapasitesi 1 litre ise, getWaterInBucket(int N, int X)X cam sayısının olduğu işlevi doldurarak N litre sıvısını boşaltırsanız, bardağa dökülen sıvı miktarını verin . Mesela eğer başında 4 litre istiyorum ve suyu 3 bardakta bulmak istiyorum, işlevgetWaterInBucket(4, 3)

Bunu programlı olarak nasıl çözebilirim? Pascal'ın üçgenini kullanarak bir matematik çözümü bulmaya çalıştım. Bu işe yaramadı. Bunun bir ağaç olduğunu düşündüm, böylelikle böyle bir parametre ekleyebilirim getWaterInBucket(BTree root, int N, int X)ve sonra her seviye için bazı özyinelemeli bir çözüm deneyebilirim, ancak parametrelere bu soruna izin verilmez. Açık bir şey var mı, bir numara mı?


18
Yönetimin sorunlarının şampanya çeşmeleriyle ilgili olduğu bir şirkette çalışmak istemem ...
mouviciel

Cam 1'den başka bir bardağa dökebilir misin? Aksi takdirde, her katman her bir bardakta eşit miktarda su olacaktır. Böylece 1, 3, 6, 10 ... litre ne zaman döktüğünüzde tam katmanlar elde edersiniz. 7 litre dökerseniz dördüncü sırada 4 bardak bulunur, böylece her biri 1/4 dolu olur. Yukarıdaki tüm katmanlar dolu olacak.
GlenPeterson,

5
@GlenPeterson Nasıl okuduğumdan, eşit bir şekilde dolduracaklarını sanmıyorum. Evet, 2 ve 3 eşit olarak doldururlar çünkü sadece içine dökülen tek bir şey vardır, ancak bir kez tam olarak 4 eşit 4/5 ve 3 eşit 5 / 6'ya dökülürler, böylece 5, 4 / 6'nın iki katı oranında doldurulur. . Merkez bardaklar her zaman dış bardaklardan daha hızlı doluyor. Zamana göre 4/6 dolu 8/9% 25 dolu ve 7/10 hala boş.
Brad

1
Ayrıca, bu bana Pascal Üçgeni'ni hatırlatıyor ..
Brad

@mouviciel Haha GlenPeterson - Dökülen ilk cam her zaman cam 1'dir. Görüşmeci ayrıca bu bilgiyi kullanabileceğini söyledi. Bu sorunun doğru cevabı olduğumdan daha fazla kafası karışmış gibiydi.
Slartibartfast

Yanıtlar:


35

Sadece, dökülenleri benzetmeniz gerekiyor,

void pour(double glasses[10], int glass, double quantity)
{
    glasses[glass] += quantity;
    if(glasses[glass] > 1.0)
    {
         double extra = glasses[glass] - 1.0;
         pour( glasses, left_glass(glass), extra / 2 );
         pour( glasses, right_glass(glass), extra / 2 );
         glasses[glass] = 1.0;
    }
}

double getWaterInGlass(int N, int X)
{
    double glasses[10] = {0,0,0,0,0,0};
    pour(glasses, 0, X);
    return glasses[N];
}

Durduğu gibi, bu bir ağaç değil. Çünkü farklı bardaklar aynı bardaklara dökülür, bu da ağaç olmasını engeller.


16
Bunun bir ağaç olmadığını gözlemlemek için +1.
Mihai Danila,

2
İyi cevap. Bir röportajda, bunun ölçeklenebilirlik problemleri olabileceğini söylemelisiniz, çünkü tüm gözlüğün içeriğini hesaplar. Ayrıca, suyun bardak alt sırasından döküldüğü durumu ele almanız gerekir. Ve istersiniz return glasses[N-1], çünkü cam sayıları 0 yerine 1'den başlar.
Tom Panning

1
Bence zor olan bölüm sağ ve sol çocukların indekslerini çözüyor olabilir. Bunu sunduysanız, görüşmeci sadece bu işlevleri uygulamanızı isteyecektir. Açık bir formül olabilir.
James

Bu gerçekten zarif bir çözüm. Teşekkürler. Her bir adımın düşünce sürecinde neyi ifade ettiğini açıklamak için kod satırlarına yorumlar eklemek için düzenleme yapabilirseniz minnettar olurum. Ayrıca gözlük sayısı 10 ile sınırlı değildir. Her şey olabilir
Slartibartfast 16

1
Sol ve sağ gözlükleri nasıl buluyorsunuz?
kiewic

7

İşte bu soruyu bir röportajda nasıl cevaplayacağımı (bu soruyu daha önce görmedim ve çözümüm bulana kadar diğer cevaplara bakmadım):

İlk önce, sadece “matematik çözümü” olarak adlandırdığınız şeyi anlamaya çalıştım ve cam 8'e geldiğimde cam 5 camdan önce taşmaya başladığı için göründüğünden daha zor olacağını fark ettim. özyineleme yolundan aşağı gitmeye karar verdi (sadece bir FYI, bir çok programlama görüşmesi sorusu özyinmek veya özmek için indüksiyon gerektirir).

Özyinelemeli düşünerek sorun daha kolay hale gelir: Cam 8'de ne kadar su vardır? 4. ve 5. bardaklardan dökülen miktarın yarısı (dolana kadar). Tabii ki, bu 4 ve 5 numaralı bardaklardan ne kadarının döküldüğünü cevaplamamız gerektiği anlamına geliyor, ancak bunun da zor olmadığı ortaya çıkıyor. Camdan 5 ne kadar dökülmüş? Ancak bunların yarısı, 2. bardaktan ve 3. bardaktan döküldüğünde, camda kalan 5 litre eksi.

Bunu tamamen (ve dağınık olarak) çözmek:

#include <iostream>
#include <cmath>
using namespace std;

double howMuchSpilledOutOf(int liters, int bucketId) {
    double spilledInto = 0.0;
    switch (bucketId) {
        case 1:
            spilledInto = liters; break;
        case 2:
            spilledInto = 0.5 * howMuchSpilledOutOf(liters, 1); break;
        case 3:
            spilledInto = 0.5 * howMuchSpilledOutOf(liters, 1); break;
        case 4:
            spilledInto = 0.5 * howMuchSpilledOutOf(liters, 2); break;
        case 5:
            spilledInto = 0.5 * howMuchSpilledOutOf(liters, 2) + 0.5 * howMuchSpilledOutOf(liters, 3); break;
        case 6:
            spilledInto = 0.5 * howMuchSpilledOutOf(liters, 3); break;
        default:
            cerr << "Invalid spill bucket ID " << bucketId << endl;
    }
    return max(0.0, spilledInto - 1.0);
}

double getWaterInBucket(int liters, int bucketId) {
    double contents = 0.0;
    switch (bucketId) {
        case 1:
            contents = liters; break;
        case 2:
            contents = 0.5 * howMuchSpilledOutOf(liters, 1); break;
        case 3:
            contents = 0.5 * howMuchSpilledOutOf(liters, 1); break;
        case 4:
            contents = 0.5 * howMuchSpilledOutOf(liters, 2); break;
        case 5:
            contents = 0.5 * howMuchSpilledOutOf(liters, 2) + 0.5 * howMuchSpilledOutOf(liters, 3); break;
        case 6:
            contents = 0.5 * howMuchSpilledOutOf(liters, 3); break;
        case 7:
            contents = 0.5 * howMuchSpilledOutOf(liters, 4); break;
        case 8:
            contents = 0.5 * howMuchSpilledOutOf(liters, 4) + 0.5 * howMuchSpilledOutOf(liters, 5); break;
        case 9:
            contents = 0.5 * howMuchSpilledOutOf(liters, 5) + 0.5 * howMuchSpilledOutOf(liters, 6); break;
        case 10:
            contents = 0.5 * howMuchSpilledOutOf(liters, 6); break;
        default:
            cerr << "Invalid contents bucket ID" << bucketId << endl;
    }
    return min(1.0, contents);
}

int main(int argc, char** argv)
{
    if (argc == 3) {
        int liters = atoi(argv[1]);
        int bucket = atoi(argv[2]);
        cout << getWaterInBucket(liters, bucket) << endl;
    }
    return 0;
}

Bu noktada (ya da bunu yazarken), görüşmeciye bunun üretimdeki ideal çözüm olmadığını söylerdim: howMuchSpilledOutOf()ve arasında yinelenen kod var getWaterInBucket(); kovaları "besleyicileri" ile eşleyen merkezi bir konum bulunmalıdır. Ancak, uygulama hızının ve doğruluğunun, uygulama ve bakım hızından daha önemli olduğu bir görüşmede (aksi belirtilmedikçe), bu çözüm tercih edilir. Daha sonra, üretim kalitesini düşündüğüm şeye daha yakın olması için kodu yeniden düzenlemeyi teklif ediyorum ve görüşmecinin karar vermesine izin verdim.

Son not: Kodumda bir yerde yazım hatası olduğuna eminim; Bunu görüşmeci için de söyleyeceğim ve yeniden düzenlemekten ya da birim testinden geçirdikten sonra kendime daha fazla güveneceğimi söyleyeceğim.


6
Bu çözüm örneğin kodlanmış. Gözlük eklemek, anahtara "vaka" eklemek demektir ... Bunun iyi bir çözüm olduğunu sanmıyorum.
Luigi Massa Gallerano

2
@ LuigiMassaGallerano - bu durumda bir röportaj sorusu olduğu için sorun değil; Mükemmel bir çözüm olmaması gerekiyordu. Görüşmeci adayın düşünce sürecini daha iyi anlamaya çalışıyor. Tom da bunu çoktan işaret ediyor this isn't the ideal solution.

1
Gerçekten değil. Sizi temin ederim ki bu senaryoda kodlama zorunluluğu yoktu. Bir görüşme sorusu sorduğumda ve görüşülen kişinin zor kodlanmış bir çözüm önerdiği bir senaryo senaryosu hazırladıysam, genel bir çözüm önermek için hazır olsa iyi olur ya da muhtemelen görüşmeyi geçemez.
Rig

5

Bunu bir ağaç problemi olarak düşünmek kırmızı bir ringa balığıdır, gerçekten yönlendirilmiş bir grafiktir. Ama bütün bunları unut.

Üstünün altında bir yerde bir bardak düşünün. Üzerine taşabilecek bir veya iki bardağı olacaktır. Uygun koordinat sistemi seçimi ile (endişelenmeyin, sonuna bakın) herhangi bir cam için "ana" gözlükleri elde etmek için bir fonksiyon yazabiliriz.

Şimdi, o bardaktan taşma durumundan bağımsız olarak, bir bardağa dökülen sıvı miktarını elde etmek için bir algoritma düşünebiliriz. Bunun cevabı, her bir ebeveynin camına depolanan miktarı eksi 2'ye bölerek çok fazla sıvı akmasıdır. Bunları sadece tüm ebeveynler için toplayın. Bunu bir miktar_poured_into () fonksiyonunun gövdesinin piton parçası olarak yazmak:

# p is coords of the current glass
amount_in = 0
for pp in parents(p):
    amount_in += max((amount_poured_into(total, pp) - 1.0)/2, 0)

Maksimum (), negatif bir taşma miktarı almadığımızdan emin olmak içindir.

Neredeyse bitti! Sayfada 'y' aşağı, birinci sıra gözlük 0, ikinci sıra 1 vb. Olan bir koordinat sistemi seçiyoruz. 'X' koordinatları üst sıra camın altında sıfır, ikinci sıra -1 ve +1, üçüncü satır -2, 0, +2 vb. Önemli olan nokta, y seviyesindeki en sol veya sağ camın abs (x) = y olacağıdır.

Bunların hepsini python (2.x) içine sarmak, biz:

def parents(p):
    """Get parents of glass at p"""

    (x, y) = p
    py = y - 1          # parent y
    ppx = x + 1         # right parent x
    pmx = x - 1         # left parent x

    if abs(ppx) > py:
        return ((pmx,py),)
    if abs(pmx) > py:
        return ((ppx,py),)
    return ((pmx,py), (ppx,py))

def amount_poured_into(total, p):
    """Amount of fluid poured into glass 'p'"""

    (x, y) = p
    if y == 0:    # ie, is this the top glass?
        return total

    amount_in = 0
    for pp in parents(p):
        amount_in += max((amount_poured_into(total, pp) - 1.0)/2, 0)

    return amount_in

def amount_in(total, p):
    """Amount of fluid left in glass p"""

    return min(amount_poured_into(total, p), 1)

Bu yüzden miktarı p deki bir bardağa almak için, miktar_ini (toplam, p) kullanın.

OP'den net değil, ancak “parametre ekleyemezsiniz” biti, orijinal sorunun gösterilen cam sayılarıyla cevaplanması gerektiği anlamına gelebilir . Bu, gösteri camı numaralarından yukarıda kullanılan dahili koordinat sistemine bir haritalama işlevi yazılarak çözülür. Fiddly, ancak yinelemeli veya matematiksel bir çözüm kullanılabilir. Anlaşılması kolay, yinelemeli işlev:

def p_from_n(n):
    """Get internal coords from glass 'number'"""

    for (y, width) in enumerate(xrange(1, n+1)):
        if n > width:
            n -= width
        else:
            x = -y + 2*(n-1)
            return (x, y)

Şimdi, bir cam sayısını kabul etmek için yukarıdaki miktar_in () işlevini yeniden yazmanız yeterlidir:

def amount_in(total, n):
    """Amount of fluid left in glass number n"""

    p = p_from_n(n)
    return min(amount_poured_into(total, p), 1)

2

İlginç.

Bu simülasyon yaklaşımını alır.

private void test() {
  double litres = 6;
  for ( int i = 1; i < 19; i++ ) {
    System.out.println("Water in glass "+i+" = "+getWater(litres, i));
  }
}

private double getWater(double litres, int whichGlass) {
  // Don't need more glasses than that.
  /*
   * NB: My glasses are numbered from 0.
   */
  double[] glasses = new double[whichGlass];
  // Pour the water in.
  pour(litres, glasses, 0);
  // Pull out the glass amount.
  return glasses[whichGlass-1];
}

// Simple non-math calculator for which glass to overflow into.
// Each glass overflows into this one and the one after.
// Only covers up to 10 glasses (0 - 9).
int[] overflowsInto = 
{1, 
 3, 4, 
 6, 7, 8, 
 10, 11, 12, 13, 
 15, 16, 17, 18, 19};

private void pour(double litres, double[] glasses, int which) {
  // Don't care about later glasses.
  if ( which < glasses.length ) {
    // Pour up to 1 litre in this glass.
    glasses[which] += litres;
    // How much overflow.
    double overflow = glasses[which] - 1;
    if ( overflow > 0 ) {
      // Remove the overflow.
      glasses[which] -= overflow;
      // Split between two.
      pour(overflow / 2, glasses, overflowsInto[which]);
      pour(overflow / 2, glasses, overflowsInto[which]+1);
    }
  }
}

Hangi baskılar (6 litre için):

Water in glass 1 = 1.0
Water in glass 2 = 1.0
Water in glass 3 = 1.0
Water in glass 4 = 0.75
Water in glass 5 = 1.0
Water in glass 6 = 0.75
Water in glass 7 = 0.0
Water in glass 8 = 0.25
Water in glass 9 = 0.25
Water in glass 10 = 0.0
...

Bu haklı gibi görünüyor.


-1

Bu binom işlevidir. Suyun N seviyesindeki bardaklar arasındaki oranı, seviyedeki her bir cam için nCr kullanılarak keşfedilebilir. Ek olarak, N seviyesinden önceki toplam gözlük sayısı, kolayca bulabileceğiniz bir formül olan 1 ila (N - 1) arasındadır. Bu nedenle, X'e göre, seviyesini belirleyebilmeli ve bardakların o seviyeye oranını kontrol etmek için nCr kullanabilmeli ve böylece X'e kadar su almaya yetecek kadar litre varsa, X'te ne kadar su olduğunu tespit edebilmelisiniz.

İkincisi, BTree'yi kullanma fikriniz gayet iyi, sadece BTree'nin harici bir parametre değil dahili bir değişken olduğu .

IOW, bu matematiği eğitiminizde ele aldıysanız (burada İngiltere'de üniversiteden önce öğretilir), o zaman bunu çok fazla sorun olmadan çözebilmelisiniz.


1
Binom fonksiyon olduğunu sanmıyorum. Binom fonksiyonunun önerdiği gibi 1,2,1 oranlarında üçüncü seviyeye ulaşır, ancak orta cam ilk önce dolar ve bundan sonra desen kırılır.
Winston Ewert,

Zaman simülasyonun bir parçası değildir ve nihai sonuçları etkilemeyecektir.
DeadMG

4
Modelleme sıvısı doldurup bardaktan akarken o zamanı korumak zorunda kalacağım simülasyonun bir parçası. 5 litrede, 4 ve 6 yarısı dolu olacak ve 5 hepsi dolu olacak. Altıncı litre eklendiğinde 8 ve 9'a dökülmeye başlanacak, ancak 7 ve 10 hiç su almayacak çünkü 4 ve 6 henüz kapasiteye ulaşmadı. Bu nedenle, binom işlevi doğru değerleri tahmin etmeyecektir.
Winston Ewert,

3
-1, bu yanlış. Seviyeler eşit şekilde doldurulmayacak.
dan_waterworth 7

Haklısın, düşünmedim. Ama bir süre düşündükten sonra, senin doğru olduğunu anladım. Bunu hesaba katacak formülü nasıl ayarlayacağınızdan emin değilsiniz.
DeadMG
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.