Bir Voronoi diyagramı oluşturun (ASCII varyantı)


24

Dikdörtgen biçiminde boş hücrelere dağılmış bazı büyük harfler verildiğini varsayalım. Dizideki her hücre , en küçük yatay ve / veya dikey adımda erişilebilen harf olarak tanımlanan, çapraz adım olmadan, kendisine en yakın harfe aittir . (Bir hücre iki veya daha fazla harften eşitse, bu harflerden hangisi ilk önce alfabetik sıradadır. Üzerinde büyük harf olan bir hücre o harfe aittir.) Sınır -hücreleri yatay veya dikey olan kendilerine ait oldukları mektuba ait olmayan bir veya daha fazla hücreye bitişik.

Bir tür Voronoi diyagramı üreten, aşağıdaki davranışla bir prosedür alt programı yazın ...

Giriş : Herhangi bir ASCII dizgisi yalnızca noktalardan, büyük harflerden ve yeni satırlardan oluşur, böylece yazdırıldığında yukarıda açıklanan türden dikdörtgen bir dizi görüntüler ve boşluklar gibi davranır.

Çıktı : Her boş sınır hücresinin girdiği harfin küçük harfli sürümüyle değiştirilen girdi dizisinin bir çıktısı. (Alt program yazdırıyor.)

örnek 1

Giriş:

......B..
.........
...A.....
.........
.......D.
.........
.C.......
.....E...
.........

Çıktı:

...ab.B..
....ab.bb
...A.abdd
aa...ad..
cca.ad.D.
..caeed..
.C.ce.edd
..ce.E.ee
..ce.....

Sınırları vurgulayan bir çizim:

Sınırları vurgulayan bir çizim

Örnek 2

Giriş:

............................U...........
......T.................................
........................................
.....................G..................
..R.......S..........F.D.E............I.
.........................H..............
.....YW.Z...............................
......X.................................
........................................
........................................
......MN...........V....................
......PQ................................
........................................
.............L...............J..........
........................................
........................................
....C...........K.......................
........................................
..................................A.....
...........B............................

Çıktı:

..rt.....ts...sg......gduu..U.....ui....
..rt..T..ts...sg......gddeu......ui.....
...rt...ts....sg......gddeeu....ui......
....rttts.....sggggggGgdde.euuuui.......
..R.rywss.S....sfffffFdDdEeeeeeei.....I.
...ryywwzs.....sf....fddhHhhhhhhhi......
..ryyYWwZzs..sssffff.fddh.......hi......
..rxxxXxzzs.sllvvvvvffddh....hhhhi......
rrrxxxxnzzssl.lv....vfddh...hjjjjii.....
mmmmmmmnnnnnl.lv.....vvdh..hj....jai....
mmmmmmMNnnnnl.lv...V...vvhhj.....jaai...
ppppppPQqqql...lv.......vhj......ja.ai..
ppppp.pq.ql....lkv.....vjj.......ja..aii
cccccppqql...L.lkkv...vj.....J...ja...aa
.....cpqqlll..lk..kvvvvj........ja......
......cccbbbllk....kkkkj.......ja.......
....C...cb..bk..K......kj.....ja........
.......cb....bk........kjjjjjja.........
......cb......bk.......kaaaaaa....A.....
.....cb....B...bk......ka...............

Renk geliştirme:

renk geliştirme


1
+ 1; ilginç; ancak, örnek giriş ve çıkışındaki hücrelerin her karakter arasında bir dolgu alanı olduğunu fark ettim. Bu bir gereklilik mi?
Doorknob

@DoorknobofSnow - Hata! Hatam - istenmeyen. Onları kaldırmak için düzenleme yapacağım.
res '

Öyleyse, açıkçası bu bir Öklid değil, bir Manhattan metrik diyagramıdır. Voronoi diyagramları Öklid dışı metrik alanlarda oldukça serin olabilir ( buraya bakın veya bir kopyasınız varsa Blender'ı ateşleyin; yerleşik bazı ilginç metrikleri vardır).
wchargin

@WChargin - Esasen, evet. Burada, iki hücre arasındaki "mesafe", bir hücreden diğerine yürümek için gereken en az adım sayısıdır, yol boyunca yalnızca yatay veya dikey olarak bitişik hücreler arasında adım yapılır. (Bu her zaman negatif olmayan bir tamsayıdır.) Hücreleri, sokakları sıfır genişliğinde ve blokları birim kareleri olan bir şehirde sokak kesişimi olarak hayal edersek , takson ölçüsüdür .
res

Yanıtlar:


5

GolfScript, 138 144 137 karakter

:^n%,,{{^n/1$=2$>1<.'.'={;{@~@+@@+\{^3$?^<n/),\,@-abs@@-abs+99*+}++^'.
'-\$1<{32+}%}++[0..1.0..(.0]2/%..&,(\0='.'if}{@@;;}if}+^n?,%puts}/

Giriş alt programa yığında tek bir dize olarak verilir. Maalesef puts, rutinin sonucu basması gerekliliği nedeniyle kullanmam gerekiyordu .

Kodun açıklaması

Dış blok, esasen giriş dikdörtgenlerinin boyutuna göre tüm konumların (x, y) üzerinde dönmektedir. Döngüde her seferinde x ve y koordinatları yığında kalır. Her satır tamamlandıktan sonra sonuç konsola yazdırılır.

:^              # save input in variable ^
n%,,{{          # split along newlines, count rows, make list [0..rows-1] 
    ???             # loop code, see below
}+^n?,%puts}/       # ^n?, count columns, make list [0..cols-1], loop and print

Döngü içinde yürütülen kod önce girişin karşılık gelen karakterini alır.

^n/                 # split input into lines
1$=                 # select the corresponding row
2$>1<               # select the corresponding col

Sonra temelde, eğer sahip olup olmadığımızı ., yani karakterin (muhtemelen) karakterini değiştirmek zorunda olup olmadığımızı kontrol ederiz.

.'.'={              # if the character is '.'
    ;               # throw away the '.'
    ???             # perform more code (see below)
}{                  # else
    @@;;            # remove coordinates, i.e. keep the current character 
                    # (i.e. A, B, ... or \n)
}if                 # end if

Yine, iç kod bir döngü ile başlar, şimdi tüm koordinatların üzerinde (x, y) (x, y + 1) (x + 1, y) (x, y-1) (x-1, y)

{                   
    @~@+@@+\        # build coordinates x+dx, y+dy
    ???             # loop code
}++                 # push coordinates before executing loop code
[0..1.0..(.0]2/%    # loop over the coordinates [0 0] [0 1] [1 0] [0 -1] [-1 0]

Son iç kod parçacığı, iki koordinat verildiğinde en yakın noktanın (küçük harf) harfini döndürür.

{                   # loop
    ^3$?^<          # find the current letter (A, B, ...) in the input string, 
                    # take anything before
    n/              # split at newlines
    ),              # from the last part take the length (i.e. column in which the letter is)
    \,              # count the number of lines remaining (i.e. row in which the letter is)
    @-abs@@-abs+    # calculate distance to the given coordinate x, y
    99*+            # multiply by 99 and add character value (used for sorting
                    # chars with equal distance)
}++                 # push the coordinates x, y
^'.
'-                  # remove '.' and newline
\$                  # now sort according to the code block above (i.e. by distance to letter)
1<{32+}%            # take the first one and make lowercase

Yani, koordinatlar için en yakın beş harften (x, y) (x, y + 1) (x + 1, y) (x, y-1) (x-1, y) hepsi değil; eşit, aksi takdirde a ..

.                   # copy five letter string
.&,(                # are there distinct letters?
\0=                 # first letter (i.e. nearest for coordinate x,y)
'.'                 # or dot
if                  # if command

Kodunuz Örnek 1’de tamamlandı, bu nedenle Örnek 2’de bazı hücrelerin yanlış yapılmasına şaşırdım: İlk üç satırın her birinde ".ui" ifadesini "ui" konumuna koyar. olması gerektiği ve dördüncü satırda "s" yi "s" yerine koyar. olmalı ve "ui" yi koyar "nerede" i " olmalıdır, vs. vs.
res

@res "Eşitlikli - ilk önce alfabetik sırada" bölümünü kaçırdım. Ne yazık ki, sıralama işlemi kararlı değil. Bunu düzeltmek için birkaç karakter eklendi.
Howard,

7

Python 3 - 424 422 417 332 295 karakter:

def v(s):
 w=s.find("\n")+1;n=(-1,1,-w,w);r=range(len(s));x=str.replace;s=x(x(s,*".~"),*"\n~")+"~"*w;t=0
 while s!=t:t=s;s=[min(s[i+j]for j in n).lower()if"~"==s[i]and(i+1)%w else s[i]for i in r]+["~"]*w
 print(x("".join(s[i]if any(s[i]!=s[i+j].lower()!="~"for j in n)else"."for i in r),*"~\n"))

Python'un sözdizimi nedeniyle, her biri kendi satırında olması gereken üç bölüm vardır:

  1. İlk satır değişkenleri ayarlar. wTahtanın bir sırasının genişliği (sonunda bir dolgu sütunu olarak geri dönüştürülecek olan yeni satır dahil). riçindeki rangetüm karakterleri indeksleyen bir nesnedir s. nBir karakterin komşularına ulaşmak için indeks ofsetlerinin bir demetidir (bu nedenle harflerin çapraz olarak yayılmasını sağlamak istiyorsanız, sadece -w-1,-w+1,w-1,w+1diziye eklemeniz gerekir ). yöntemi xiçin kısa bir addır str.replace, daha sonra kodda birkaç kez kullanılır (çağrılar garip görünür, çünkü x(s,*"xy")geleneksel değil karakterleri kaydetmek için kullanıyorum s.replace("x", "y")). sOnun ile parametre dizisi, hem de bu noktada biraz değiştirilmiş bir .karakter ve yeni satırlar yerini~karakterler (çünkü tüm harflerden sonra sıralanırlar). ~Sonuna bir satırın doldurma karakterleri de eklenir. tdaha sonra "eski" versiyonunun referansı olarak kullanılacaktır s, fakat başlangıçta eşit olmayan birşeye ssıfırlanmalı ve sıfır sadece bir karakter alır (daha fazla bir Pythonic değeri olacaktır None, fakat bu üç fazla karakter) .
  2. İkinci satırda sbir liste kavrama kullanarak art arda güncellenen bir döngü vardır . Anlama indeksleri üzerinde ilerledikçe s, ~karakterlerin yerine minkomşuları gelir. Bir ~karakter tamamen diğerleriyle çevriliyse ~, bu hiçbir şey yapmaz. Bir veya daha fazla harf yanında olsaydı, (lehine bunların en küçük olacak "a"üzerinde "b"vs.). ~Karakterlere dönüştürülen yeni satırlar, indeksleri modulus operatörü ile tespit edilerek korunur. Sonunda bulunan doldurma sırası, liste kavramada güncellenmez (çünkü indekslerin aralığı, reklenmeden önce hesaplanır s). Bunun yerine, taze bir satır~Kavrama yapıldıktan sonra karakterler eklenir. Not skarakterlerin listesini ziyade döngünün ilk geçişten sonra bir dize olur (ancak Python biz hala endeksi aynı şekilde karakter almak için olabilir türleri hakkında esnek olduğu için).
  3. Son satır, diyagramın iç kısımlarını çıkarır ve karakterleri yazdırılacak bir dizgiye yeniden oluşturur. İlk önce, yalnızca kendi diğer kopyalarıyla (veya ~dolgudan gelen karakterler) çevrelenen herhangi bir harf, yerine geçer .. Sonra, karakterlerin hepsi bir araya getirilerek tek bir dizgede birleştirilir. Son olarak, dolgu ~karakterleri tekrar yeni satırlara dönüştürülür ve dize yazdırılır.

Muhtemelen, r=rangeçağrılabilir bir prosedürün bir parçası olarak kabul edilmesi gereken fonksiyon gövdesinde olması gerekir, ancak karakterleri yazarak kaydedebilirsiniz r=range;s=[l.replace. Ayrıca yazılı tarafından daha fazla karakter sıkmak olabilir if"~"==s[y][x]elseve if"~"==s[y][x]else(Python 2.7 ile benim için Btw bu ran) 422. üzere toplam,
res

@res: Bu öneriler için teşekkürler. r=rangeİşlevin ilk satırının sonuna (diğer değişkenleri kurduğum yere) koydum ve daha önce özlediğim birkaç alanı kestim. Bahsettiğin ikisini de aldığımdan emin değilim, aynı şeyi iki kez söylediğin gibi. Ayrıca, Python 2.7'de parantezlere ihtiyacınız olmadığından print(genellikle sadece 1 karakter kaydeder, ancak print"\n".join(...)çalışır) , çünkü iki karakter daha kısa olabilir .
Blckknght

Hata! O ikinciyi yanlış yapıştırdım. Olması gerekiyordu s[y][x]for(bir boşluk kaldırarak), ama yine de bulmuş gibisin.
res

Evet, sahip olduğum diğeri. Daha büyük bir değişiklik denemeye karar verdim ve 2d yerine 1d'ye gittim, ki bu da bir sürü karakteri kurtardı!
Blckknght

3

Python, 229 226 karakter

def F(s):
 e,f,b='~.\n';N=s.index(b)+1;s=s.replace(f,e)
 for i in 2*N*e:s=''.join(min([x[0]]+[[y.lower()for y in x if y>b],all(y.lower()in f+b+x[0]for y in x)*[f]][x[0]!=e])for x in zip(s,s[1:]+b,s[N:]+b*N,b+s,b*N+s))
 print s

F("""......B..
.........
...A.....
.........
.......D.
.........
.C.......
.....E...
.........
""")

Sonucu hesaplamak için bir sel doldurur mu. İzleyen for/ zipaçılan x, o hücrede bulunan değeri ve dört komşusunu içeren her hücre için bir dizi oluşturur . Sonra Blckknght'ın numarasını ve minher hücre için bir sürü olasılık kullanıyoruz. Bunlar orijinal hücre değeri, hücre henüz ziyaret edilmediyse herhangi bir komşu, ya da ziyaret .edilmişse ve tüm komşular .hücrenin kendisine eşit veya eşittir.


Alt program baskı yapmak gerekiyordu olduğundan, sadece değiştirebilir return siçin print s. Ayrıca, y!=bdeğiştirilemez y>bmi? Sanırım bu 226 karakter olur.
res

3

İşte burada. Bu benim ilk F # programım. Dilin bir özelliğini kaçırdıysam, lütfen hala öğrendiğim gibi beni uyar.

İşte benim örnek girdi

 . . . . . . . . . . . . . . . . . . . . . . . . .
 . . . . . . . . . . . . B . . . . . . . . . . . .
 . . . . . . . . . . . . . . . . . . . . . . . . .
 . . . . . . . . A . . . . . . . . . . . . . . . .
 . . . . . . . . . . . . . . . . . . . . . . . . .
 . . . . . . . . . . . . . . . . C . . . . . . . .
 . . . . . . . . . . . . . . . . . . . . . . . . .
 . . . . . . . . . . . . . . . . . . . G . . . . .
 . . . . . . . D . . . . . . . . . . . . . . . . .
 . . . . . . . . F . . . . . . . . . . . . . . . .
 . . . . . . . E . . . . . . . . . . . . . . . . .
 . . . . . . . . . . . . . . . . . . . . . . . . .
 . . . . . . . . . . . . . . . . . . . . . . . . .
 . . . . . . . . . . . . . . . . . . . . . . . . .

İşte çıktı

 . . . . . . . . . a b . . . . . . . b g . . . . .
 . . . . . . . . . a b . B . . . b b b g . . . . .
 . . . . . . . . . . a b . . . b c c c g . . . . .
 . . . . . . . . A . . a b . b c . . c g . . . . .
 . . . . . . . . . . . a b b c . . . c g . . . . .
 a a a a a a a a . . . a b c . . C . c g . . . . .
 d d d d d d d d a a a a b c . . . c g . . . . . .
 . . . . . . . . d d d d b c . . c g . G . . . . .
 . . . . . . . D d d d d d c . . c g . . . . . . .
 d d d d d d d d f f f f f f c . c g . . . . . . .
 e e e e e e e e e e e e e e c . c g . . . . . . .
 . . . . . . . . . . . . . e c . c g . . . . . . .
 . . . . . . . . . . . . . e c . c g . . . . . . .
 . . . . . . . . . . . . . e c . c g . . . . . . .

İşte kod. Keyfini çıkarın.

// The first thing that we need is some data. 
let originalData = [
     "........................."
     "............B............" 
     "........................." 
     "........A................" 
     "........................." 
     "................C........"          
     "........................." 
     "...................G....." 
     ".......D................." 
     "........F................"           
     ".......E................."          
     "........................."
     "........................."
     "........................."
     ]

Şimdi bu verileri çift boyutlu bir diziye dönüştürmemiz gerekiyor, böylece indeksleyicilerden erişebiliyoruz.

let dataMatrix = 
    originalData
    |> List.map (fun st -> st.ToCharArray())
    |> List.toArray

// We are going to need a concept of ownership for each
// cell. 
type Owned = 
    | Unclaimed
    | Owner of char
    | Claimed of char
    | Boundary of char

Her hücrenin sahipliğini temsil eden bir matris oluşturalım

let claims =
    dataMatrix
    |> Array.map (fun row ->
        row
        |> Array.map (function
            | '.' -> Owned.Unclaimed
            | ch -> Owned.Owner(ch))
        )

Ne olduğunu görmek için bize yardımcı bir yöntem verelim.

let printIt () =
    printfn ""
    claims
    |> Array.iter (fun row ->
        row |> Array.iter (function
            | Owned.Claimed(ch) -> printf " ." 
            | Owned.Owner(ch) -> printf " %c" ch
            | Owned.Boundary(ch) -> printf " %c" ch
            | _ -> printf " ." )
        printfn "")            

Belirli bir büyük harfin bulunduğu yeri temsil etmek için bir kayıt oluşturalım.

type CapitalLocation = { X:int; Y:int; Letter:char }

Şimdi büyük harflerin hepsini bulmak istiyoruz.

let capitals = 
    dataMatrix
    |> Array.mapi (fun y row -> 
        row 
        |> Array.mapi (fun x item -> 
            match item with
            | '.' -> None
            | _ -> Some({ X=x; Y=y; Letter=item }))
        |> Array.choose id
        |> Array.toList
        )
    |> Array.fold (fun acc item -> item @ acc) List.empty<CapitalLocation>
    |> List.sortBy (fun item -> item.Letter)

Hareket ettikçe, bir yön kavramına ihtiyacımız var.

type Direction =
    | Left = 0
    | Up = 1
    | Right = 2
    | Down = 3   

// Function gets the coordinates of the adjacent cell. 
let getCoordinates (x, y) direction =
    match direction with
    | Direction.Left -> x-1, y
    | Direction.Up -> x, y-1
    | Direction.Right -> x+1, y
    | Direction.Down -> x, y+1
    | _ -> (-1,-1) // TODO: Figure out how to best throw an error here. 

Hareket ettikçe, büyüklük hakkında bilmemiz gerekecek. Bu, sınırların dışına çıkıp çıkmadığımızı izlememize yardımcı olacaktır.

type Size = { Width:int; Height: int }    

// Get the size of the matrix. 
let size = {Width=originalData.Head.Length; Height=originalData.Length}

Aktif Desen: verilen bir hücrenin ölçütleriyle eşleşir.

let (|OutOfBounds|UnclaimedCell|Claimed|Boundary|) (x,y) =
    match (x,y) with 
    | _,_ when x < 0 || y < 0 -> OutOfBounds
    | _,_ when x >= size.Width || y >= size.Height -> OutOfBounds
    | _ ->                     
        match claims.[y].[x] with
        | Owned.Unclaimed -> UnclaimedCell(x,y)
        | Owned.Claimed(ch) -> Claimed(x,y,ch)
        | Owned.Boundary(ch) -> Boundary(x,y,ch)
        | Owned.Owner(ch) -> Claimed(x,y,ch)

Şimdi pirinç vergisine gidiyoruz. Bu hücre iddia ediyor!

let claimCell letter (x, y) =         
    // Side effect: Change the value of the cell
    (claims.[y].[x] <- Owned.Claimed (System.Char.ToLower letter)) |> ignore

Aktif modeli kullanarak, talep edilmezse bu hücreyi talep edin ve bitişik hücrelerin koordinatlarını döndürün.

let claimAndReturnAdjacentCells (letter, coordinates, direction) =
    match coordinates with 
    | UnclaimedCell (x,y) ->         
        // Claim it and return the Owned object.
        claimCell letter coordinates // meaningful side effect
        // use Direction as int to allow math to be performed. 
        let directionInt = int direction;            
        Some(
            // [counter-clockwise; forward; clockwise]
            [(directionInt+3)%4; directionInt; (directionInt+1)%4]                 
            |> List.map enum<Direction>                 
            |> List.map (fun newDirection -> 
                (
                    letter, 
                    getCoordinates coordinates newDirection, 
                    newDirection
                ))
        )
    | Claimed(cx,cy,cch) when cch <> System.Char.ToLower letter-> 
        // If we find a "Claimed" element that is not our letter, we have 
        // hit a boundary. Change "Claimed" to "Boundary" and return the 
        // element that led us to evaluating this element. It is also a 
        // boundary. 
        (claims.[cy].[cx] <- Owned.Boundary (System.Char.ToLower cch)) |> ignore
        let reverseDirection = enum<Direction>(((int direction)+2)%4)
        Some[(
            cch,
            getCoordinates (cx, cy) reverseDirection,
            reverseDirection
        )]
    | _ -> None

Bu veri torbasının listelerini oluşturmaya başlıyoruz, işleri daha net hale getirmek için bir tür oluşturalım.

type CellClaimCriteria = (char * (int * int) * Direction)

Talep eden hücreler için bir kriter listesi göz önüne alındığında, bir sonraki hücreleri talep etmek ve bu listeye geri almak için geri dönen listeyi tekrarlayacağız.

let rec claimCells (items:CellClaimCriteria list) =
    items
    |> List.fold (fun acc item ->
        let results = claimAndReturnAdjacentCells item 
        if Option.isSome(results) 
        then (acc @ Option.get results) 
        else acc
        ) List.empty<CellClaimCriteria> 
    |> (fun l ->            
        match l with
        | [] -> []
        | _ -> claimCells l)

Her sermaye için, her yönde bir talep kriteri oluşturun ve daha sonra bu hücreleri tekrar tekrar talep edin.

let claimCellsFromCapitalsOut ()=
    capitals
    |> List.fold (fun acc capital ->
        let getCoordinates = getCoordinates (capital.X, capital.Y)
        [Direction.Left; Direction.Up; Direction.Right; Direction.Down]
        |> List.map (fun direction ->                
            (
                capital.Letter, 
                getCoordinates direction, 
                direction
            ))
        |> (fun items -> acc @ items)) List.empty<CellClaimCriteria>
    |> claimCells

Her programın bir ana ihtiyacı vardır.

[<EntryPoint>]
let main args = 
    printIt()
    claimCellsFromCapitalsOut()
    printIt()
    0

Aşina olmadığınız bir dilde çalışan bir çözüm bulmak için çok iyi. Ancak, son adımı kaçırdınız: bu kod-golf , yani amaç mümkün olan en kısa programı yazmaktır: tek karakterli tanımlayıcılar, yalnızca derlemek için kesinlikle gerekli olan boşluk, vb.
Peter Taylor

3
PeterTlorlor haklısın. Onu özledim. Bu site daha fazla "Programlama Bulmaca" ve daha az "Kod Golf" gerektiriyor.
Phillip Scott Givens,
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.