Pixel yang dipisahkan secara unik


30

Untuk gambar N demi N , temukan satu set piksel sehingga tidak ada jarak pemisahan yang muncul lebih dari satu kali. Yaitu, jika dua piksel dipisahkan oleh jarak d , maka mereka hanya dua piksel yang dipisahkan dengan tepat d (menggunakan jarak Euclidean ). Perhatikan bahwa d tidak perlu bilangan bulat.

Tantangannya adalah menemukan set yang lebih besar daripada orang lain.

Spesifikasi

Tidak diperlukan input - untuk kontes ini N akan diperbaiki pada 619.

(Karena orang terus bertanya - tidak ada yang istimewa tentang nomor 619. Dipilih menjadi cukup besar untuk membuat solusi optimal tidak mungkin, dan cukup kecil untuk membiarkan gambar N demi N ditampilkan tanpa Stack Exchange secara otomatis mengecilkannya. Gambar dapat ditampilkan ukuran penuh hingga 630 kali 630, dan saya memutuskan untuk menggunakan prime terbesar yang tidak melebihi itu.)

Outputnya adalah daftar integer yang dipisahkan oleh spasi.

Setiap bilangan bulat dalam output mewakili salah satu piksel, diberi nomor dalam urutan bacaan bahasa Inggris dari 0. Misalnya untuk N = 3, lokasi akan diberi nomor dalam urutan ini:

0 1 2
3 4 5
6 7 8

Anda dapat menampilkan informasi kemajuan selama menjalankan jika Anda inginkan, selama hasil penilaian akhir mudah tersedia. Anda dapat output ke STDOUT atau ke file atau apa pun yang paling mudah untuk menempel ke Hakim Potongan Stack di bawah ini.

Contoh

N = 3

Koordinat yang Dipilih:

(0,0)
(1,0)
(2,1)

Keluaran:

0 1 5

Kemenangan

Skor adalah jumlah lokasi dalam output. Dari jawaban yang valid yang memiliki skor tertinggi, yang paling awal untuk memposting output dengan skor yang menang.

Kode Anda tidak perlu deterministik. Anda dapat memposting output terbaik Anda.


Bidang terkait untuk penelitian

(Terima kasih kepada Abulafia untuk tautan Golomb)

Meskipun tidak satu pun dari keduanya yang sama dengan masalah ini, keduanya memiliki konsep yang sama dan dapat memberi Anda gagasan tentang cara mendekati ini:

Perhatikan bahwa poin yang diperlukan untuk pertanyaan ini tidak tunduk pada persyaratan yang sama dengan persegi panjang Golomb. Persegi panjang Golomb memanjang dari case 1 dimensi dengan mensyaratkan bahwa vektor dari setiap titik menjadi unik. Ini berarti bahwa ada dua titik yang dipisahkan oleh jarak 2 secara horizontal, dan juga dua titik dipisahkan oleh jarak 2 secara vertikal.

Untuk pertanyaan ini, jarak skalar yang harus unik, sehingga tidak boleh ada pemisahan horizontal dan vertikal 2. Setiap solusi untuk pertanyaan ini adalah persegi panjang Golomb, tetapi tidak setiap persegi panjang Golomb akan menjadi solusi yang valid untuk pertanyaan ini.


Batas atas

Dennis membantu menunjukkan dalam obrolan bahwa 487 adalah batas atas skor, dan memberikan bukti:

Menurut kode CJam saya ( 619,2m*{2f#:+}%_&,), ada 1.118.000 angka unik yang dapat ditulis sebagai jumlah kuadrat dari dua bilangan bulat antara 0 dan 618 (keduanya termasuk). n piksel memerlukan n (n-1) / 2 jarak unik antara satu sama lain. Untuk n = 488, itu memberi 118828.

Jadi ada 118.800 kemungkinan panjang berbeda antara semua piksel potensial dalam gambar, dan menempatkan 488 piksel hitam akan menghasilkan 118.828 panjang, yang membuat mustahil bagi mereka semua untuk menjadi unik.

Saya akan sangat tertarik untuk mendengar jika ada yang punya bukti batas atas yang lebih rendah dari ini.


Papan peringkat

(Jawaban terbaik oleh setiap pengguna)

gambar papan peringkat


Hakim Stack Snippet


Saya ingin sekali melihat jawaban Piet di sini
C5H8NNaO4

@ C5H8NNaO4 kompetisi terbuka - tidak ada yang mendekati solusi optimal sehingga ada banyak ruang untuk jawaban baru ...
trichoplax

Karena Anda menawarkan hadiah untuk piksel batas atas dan eksperimental yang terbukti, saya berasumsi ada beberapa jenis aplikasi untuk masalah ini?
Fatalkan tanggal

@Fatalize bukan yang saya sadari, tapi saya akan terpesona mendengarnya. Masalah serupa array Costas memiliki aplikasi praktis yang terdaftar tetapi saya tidak menemukan apa pun pada masalah khusus ini.
trichoplax

1
Saya telah melihat ini, dan saya percaya n = 487 menjadi batas atas minimal pada piksel. Karena penasaran, apakah Anda akan menerima bukti bahwa tidak ada batas atas yang lebih rendah untuk hadiah?
Mego

Jawaban:


13

Python 3, 135 136 137

10 6830 20470 47750 370770 148190 306910 373250 267230 354030 30390 361470 118430 58910 197790 348450 381336 21710 183530 305050 2430 1810 365832 99038 381324 39598 262270 365886 341662 15478 9822 365950 44526 58862 24142 381150 31662 237614 118830 380846 7182 113598 306750 11950 373774 111326 272358 64310 43990 200278 381014 165310 254454 12394 382534 87894 6142 750 382478 15982 298326 70142 186478 152126 367166 1162 23426 341074 7306 76210 140770 163410 211106 207962 35282 165266 300178 120106 336110 30958 158 362758 382894 308754 88434 336918 244502 43502 54990 279910 175966 234054 196910 287284 288468 119040 275084 321268 17968 2332 86064 340044 244604 262436 111188 291868 367695 362739 370781 375723 360261 377565 383109 328689 347879 2415 319421 55707 352897 313831 302079 19051 346775 361293 328481 35445 113997 108547 309243 19439 199037 216463 62273 174471 207197 167695 296927

Ditemukan menggunakan algoritma serakah yang, pada setiap tahap, memilih piksel yang valid yang set jaraknya ke piksel yang dipilih tumpang tindih paling sedikit dengan piksel lainnya.

Secara khusus, skornya adalah

score(P) = sum(number of pixels with D in its distance set
               for each D in P's distance set)

dan piksel dengan skor terendah dipilih.

Pencarian dimulai dengan titik 10(yaitu (0, 10)). Bagian ini dapat disesuaikan, jadi mulai dengan piksel yang berbeda dapat menyebabkan hasil yang lebih baik atau lebih buruk.

Algoritma ini cukup lambat, jadi saya mencoba menambahkan optimisasi / heuristik, dan mungkin beberapa langkah mundur. PyPy direkomendasikan untuk kecepatan.

Siapa pun yang mencoba membuat algoritme harus mencoba N = 10, yang saya dapatkan 9 (tetapi ini membutuhkan banyak penyesuaian dan mencoba titik awal yang berbeda):

masukkan deskripsi gambar di sini

Kode

from collections import Counter, defaultdict
import sys
import time

N = 619

start_time = time.time()

def norm(p1, p2):
    return (p1//N - p2//N)**2 + (p1%N - p2%N)**2

selected = [10]
selected_dists = {norm(p1, p2) for p1 in selected for p2 in selected if p1 != p2}
pix2dist = {} # {candidate pixel: {distances to chosen}}
dist2pix = defaultdict(set)

for pixel in range(N*N):
    if pixel in selected:
        continue

    dist_list = [norm(pixel, p) for p in selected]
    dist_set = set(dist_list)

    if len(dist_set) != len(dist_list) or dist_set & selected_dists:
        continue

    pix2dist[pixel] = dist_set

    for dist in dist_set:
        dist2pix[dist].add(pixel)

while pix2dist:
    best_score = None
    best_pixel = None

    for pixel in sorted(pix2dist): # Sorting for determinism
        score = sum(len(dist2pix[d]) for d in pix2dist[pixel])

        if best_score is None or score < best_score:
            best_score = score
            best_pixel = pixel

    added_dists = pix2dist[best_pixel]
    selected_dists |= added_dists
    del pix2dist[best_pixel]
    selected.append(best_pixel)

    for d in added_dists:
        dist2pix[d].remove(best_pixel)

    to_remove = set()
    for pixel in pix2dist:
        new_dist = norm(pixel, best_pixel)

        if (new_dist in selected_dists or new_dist in pix2dist[pixel]
                or added_dists & pix2dist[pixel]):
            to_remove.add(pixel)
            continue

        pix2dist[pixel].add(new_dist)
        dist2pix[new_dist].add(pixel)

    for pixel in to_remove:
        for d in pix2dist[pixel]:
            dist2pix[d].remove(pixel)

        del pix2dist[pixel]

    print("Selected: {}, Remaining: {}, Chosen: ({}, {})".format(len(selected), len(pix2dist),
                                                                 best_pixel//N, best_pixel%N))
    sys.stdout.flush()

print(*selected)
print("Time taken:", time.time() - start_time)

3
Saya dengan cepat memaksa, N=10dan ada banyak tata letak yang berbeda dengan 9 poin tetapi itu yang terbaik yang bisa Anda lakukan.
Will

5

SWI-Prolog, skor 131

Hampir tidak lebih baik daripada jawaban awal, tapi saya kira ini akan membuat segalanya mulai lebih. Algoritma ini sama dengan jawaban Python kecuali fakta bahwa ia mencoba piksel dengan cara alternatif, dimulai dengan piksel kiri atas (piksel 0), kemudian piksel kanan bawah (piksel 383160), lalu piksel 1, kemudian piksel 383159 , dll.

a(Z) :-
    N = 619,
    build_list(N,Z).

build_list(N,R) :-
    M is N*N,
    get_list([M,-1],[],L),
    reverse(L,O),
    build_list(N,O,[],[],R).

get_list([A,B|C],R,Z) :-
    X is A - 1,
    Y is B + 1,
    (X =< Y,
    Z = R
    ;
    get_list([X,Y,A,B|C],[X,Y|R],Z)).

build_list(_,[],R,_,R) :- !.
build_list(N,[A|T],R,W,Z) :-
    separated_pixel(N,A,R,W,S),
    is_set(S),
    flatten([W|S],V),!,
    build_list(N,T,[A|R],V,Z)
    ;build_list(N,T,R,W,Z).


separated_pixel(N,A,L,W,R) :-
    separated_pixel(N,A,L,[],W,R).

separated_pixel(N,A,[A|T],R,W,S) :-
        separated_pixel(N,A,T,R,W,S).

separated_pixel(N,A,[B|T],R,W,S) :-
    X is (A mod N - B mod N)*(A mod N - B mod N),
    Y is (A//N - B//N)*(A//N - B//N),
    Z is X + Y,
    \+member(Z,W),
    separated_pixel(N,A,T,[Z|R],W,S).

separated_pixel(_,_,[],R,_,R).

Memasukkan:

a(A).

Keluaran:

Z = [202089, 180052, 170398, 166825, 235399, 138306, 126354, 261759, 119490, 117393, 281623, 95521, 290446, 299681, 304310, 78491, 314776, 63618, 321423, 60433, 323679, 52092, 331836, 335753, 46989, 40402, 343753, 345805, 36352, 350309, 32701, 32470, 352329, 30256, 28089, 357859, 23290, 360097, 22534, 362132, 20985, 364217, 365098, 17311, 365995, 15965, 15156, 368487, 370980, 371251, 11713, 372078, 372337, 10316, 373699, 8893, 374417, 8313, 7849, 7586, 7289, 6922, 376588, 6121, 5831, 377399, 377639, 4941, 378494, 4490, 379179, 3848, 379453, 3521, 3420, 379963, 380033, 3017, 380409, 2579, 380636, 2450, 2221, 2006, 381235, 1875, 381369, 381442, 381682, 1422, 381784, 1268, 381918, 1087, 382144, 382260, 833, 382399, 697, 382520, 622, 382584, 382647, 382772, 384, 382806, 319, 286, 382915, 382939, 190, 172, 383005, 128, 383050, 93, 383076, 68, 383099, 52, 40, 383131, 21, 383145, 10, 383153, 4, 383158, 1, 383160, 0]

Gambar dari Stack Snippet

131 poin


Karena ada maksimum teoritis 487, bahkan peningkatan tambahan adalah signifikan ...
trichoplax

Apakah output Anda seperti yang ditunjukkan berfungsi dengan Stack Snippet? Saya telah menentukan ruang yang terpisah (seperti pada contoh jawaban saya) tetapi alasan utama untuk itu adalah agar Stack Snippet akan berfungsi.
trichoplax

@trichoplax Ya itu salah ketik, saya mulai dengan pixel 0, saya akan memperbaikinya. Untuk mendapatkan gambar saya memilih bagian dari output antara dua tanda kurung, dan menghapus semua koma. Cuplikan Stack tampaknya bekerja dengan piksel yang dipisahkan koma.
Fatalkan

4

Haskell— 115 130 131 135 136

Inspirasi saya adalah Saringan Eratosthenes dan khususnya The Saringan Asli Eratosthenes , sebuah makalah oleh Melissa E. O'Neill dari Harvey Mudd College. Versi asli saya (yang dianggap sebagai poin dalam urutan indeks) menyaring poin dengan sangat cepat, untuk beberapa alasan saya tidak dapat mengingat saya memutuskan untuk mengacak poin sebelum "mengayak" mereka dalam versi ini (saya pikir semata-mata untuk membuat menghasilkan jawaban yang berbeda lebih mudah dengan menggunakan benih baru di generator acak). Karena poin tidak lagi dalam urutan apa pun, tidak ada penyaringan yang sebenarnya terjadi lagi, dan sebagai hasilnya diperlukan beberapa menit hanya untuk menghasilkan jawaban 115 poin tunggal ini. Sebuah KO Vectormungkin akan menjadi pilihan yang lebih baik sekarang.

Jadi dengan versi ini sebagai pos pemeriksaan saya melihat dua cabang, kembali ke algoritma "Saringan Asli" dan memanfaatkan daftar monad untuk pilihan, atau mengganti Setoperasi untuk padanan yang setara Vector.

Sunting: Jadi untuk versi dua bekerja saya beralih kembali ke algoritma ayakan, meningkatkan generasi "kelipatan" (mengetuk indeks dengan menemukan titik pada koordinat bilangan bulat pada lingkaran dengan jari-jari yang sama dengan jarak antara dua titik, mirip dengan menghasilkan kelipatan utama) ) dan membuat beberapa perbaikan waktu konstan dengan menghindari beberapa perhitungan ulang yang tidak perlu.

Untuk beberapa alasan saya tidak dapat mengkompilasi ulang dengan profil yang dihidupkan, tetapi saya percaya bahwa hambatan utama sekarang adalah kemunduran. Saya pikir mengeksplorasi sedikit paralelisme dan konkurensi akan menghasilkan peningkatan kecepatan linear, tetapi kehabisan memori mungkin akan membuat saya mengalami peningkatan 2x.

Sunting: Versi 3 sedikit berkelok-kelok, saya pertama kali bereksperimen dengan heuristik dalam mengambil indeks 𝐧 berikutnya (setelah menyaring dari pilihan sebelumnya) dan memilih yang menghasilkan set KO minimum berikutnya. Ini berakhir menjadi terlalu lambat, jadi saya kembali ke metode brute force seluruh-pencarian. Suatu ide untuk memesan titik-titik dengan jarak dari beberapa asal datang kepada saya, dan menyebabkan peningkatan dengan satu titik (pada saat kesabaran saya bertahan). Versi ini memilih indeks 0 sebagai asal, mungkin ada baiknya mencoba titik tengah pesawat.

Sunting: Saya mengambil 4 poin dengan memesan ulang ruang pencarian untuk memprioritaskan poin yang paling jauh dari pusat. Jika Anda menguji kode saya, 135 136 sebenarnya adalah solusi ketiga kedua yang ditemukan. Edit cepat: Versi ini tampaknya paling mungkin tetap produktif jika dibiarkan berjalan. Saya kira saya mungkin mengikat pada 137, kemudian kehabisan kesabaran menunggu 138.

Satu hal yang saya perhatikan (yang mungkin bisa membantu seseorang) adalah bahwa jika Anda mengatur titik pemesanan dari pusat pesawat (yaitu, menghapus (d*d -)dari originDistance) gambar yang terbentuk terlihat sedikit seperti spiral prima yang jarang.

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE BangPatterns #-}

module Main where

import Data.Function (on)
import Data.List     (tails, sortBy)
import Data.Maybe    (fromJust)
import Data.Ratio
import Data.Set      (fromList, toList, union, difference, member)

import System.IO

sideLength :: Int
sideLength = 619

data Point = Point {  x :: !Int,  y :: !Int } deriving (Ord, Eq)
data Delta = Delta { da :: !Int, db :: !Int }

euclidean :: Delta -> Int
euclidean Delta{..} = da*da + db*db

instance Eq Delta where
  (==) = (==) `on` euclidean

instance Ord Delta where
  compare = compare `on` euclidean

delta :: Point -> Point -> Delta
delta a b = Delta (min dx dy) (max dx dy)
  where
    dx = abs (x a - x b)
    dy = abs (y a - y b)

equidistant :: Dimension -> Point -> Point -> [Point]
equidistant d a b =
  let
    (dx, dy) = (x a - x b, y a - y b)
    m = if dx == 0 then Nothing else Just (dy % dx)                    -- Slope
    w = if dy == 0 then Nothing else Just $ maybe 0 (negate . recip) m -- Negative reciprocal
    justW = fromJust w -- Moral bankruptcy
    (px, py) = ((x a + x b) % 2, (y a + y b) % 2)                      -- Midpoint
    b0 = py - (justW * px)                                             -- Y-intercept
    f q = justW * q + b0                                               -- Perpendicular bisector
  in
   maybe (if denominator px == 1 then map (Point (numerator px)) [0..d - 1] else [])
         ( map (\q -> Point q (numerator . f . fromIntegral $ q))
         . filter ((== 1) . denominator . f . fromIntegral)
         )
         (w >> return [0..d - 1])

circle :: Dimension -> Point -> Delta -> [Point]
circle d p delta' =
  let
    square = (^(2 :: Int))
    hypoteneuse = euclidean delta'
    candidates = takeWhile ((<= hypoteneuse) . square) [0..d - 1]
    candidatesSet = fromList $ map square [0..d - 1]
    legs = filter ((`member` candidatesSet) . (hypoteneuse -) . square) candidates
    pythagoreans = zipWith Delta legs
                 $ map (\l -> floor . sqrt . (fromIntegral :: Int -> Double) $ hypoteneuse - square l) legs
  in
    toList . fromList $ concatMap (knight p) pythagoreans

knight :: Point -> Delta -> [Point]
knight Point{..} Delta{..} =
    [ Point (x + da) (y - db), Point (x + da) (y + db)
    , Point (x + db) (y - da), Point (x + db) (y + da)
    , Point (x - da) (y - db), Point (x - da) (y + db)
    , Point (x - db) (y - da), Point (x - db) (y + da)
    ]

type Dimension = Int
type Index = Int

index :: Dimension -> Point -> Index
index d Point{..} = y * d + x

point :: Dimension -> Index -> Point
point d i = Point (i `rem` d) (i `div` d)

valid :: Dimension -> Point -> Bool
valid d Point{..} = 0 <= x && x < d
                 && 0 <= y && y < d

isLT :: Ordering -> Bool
isLT LT = True
isLT _  = False

sieve :: Dimension -> [[Point]]
sieve d = [i0 : sieve' is0 [i0] [] | (i0:is0) <- tails . sortBy originDistance . map (point d) $ [0..d*d - 1]]
  where
    originDistance :: Point -> Point -> Ordering
    originDistance = compare `on` ((d*d -) . euclidean . delta (point d (d*d `div` 2)))

    sieve' :: [Point] -> [Point] -> [Delta] -> [Point]
    sieve' []     _  _ = []
    sieve' (i:is) ps ds = i : sieve' is' (i:ps) ds'
      where
        ds' = map (delta i) ps ++ ds
        knockouts = fromList [k | d' <- ds
                                , k  <- circle d i d'
                                , valid d k
                                , not . isLT $ k `originDistance` i
                                ]
            `union` fromList [k | q  <- i : ps
                                , d' <- map (delta i) ps
                                , k  <- circle d q d'
                                , valid d k
                                , not . isLT $ k `originDistance` i
                                ]
            `union` fromList [e | q <- ps
                                , e <- equidistant d i q
                                , valid d e
                                , not . isLT $ e `originDistance` i
                                ]
        is' = sortBy originDistance . toList $ fromList is `difference` knockouts

main :: IO ()
main = do let answers = strictlyIncreasingLength . map (map (index sideLength)) $ sieve sideLength
          hSetBuffering stdout LineBuffering
          mapM_ (putStrLn . unwords . map show) $ answers
  where
    strictlyIncreasingLength :: [[a]] -> [[a]]
    strictlyIncreasingLength = go 0
      where
        go _ []     = []
        go n (x:xs) = if n < length x then x : go (length x) xs else go n xs

Keluaran

1237 381923 382543 382541 1238 1857 380066 5 380687 378828 611 5571 382553 377587 375113 3705 8664 376356 602 1253 381942 370161 12376 15475 7413 383131 367691 380092 376373 362114 36 4921 368291 19180 382503 26617 3052 359029 353451 29716 382596 372674 352203 8091 25395 12959 382479 381987 35894 346031 1166 371346 336118 48276 2555 332400 46433 29675 380597 13066 382019 1138 339859 368230 29142 58174 315070 326847 56345 337940 2590 382663 320627 70553 19278 7309 82942 84804 64399 5707 461 286598 363864 292161 89126 371267 377122 270502 109556 263694 43864 382957 824 303886 248218 18417 347372 282290 144227 354820 382909 380301 382808 334361 375341 2197 260623 222212 196214 231526 177637 29884 251280 366739 39442 143568 132420 334718 160894 353132 78125 306866 140600 297272 54150 240054 98840 219257 189278 94968 226987 265881 180959 142006 218763 214475

Peningkatan yang mengesankan. Anda memiliki 2 jam lagi untuk mencapai 138 sebelum hadiah diberikan. Cara yang bagus ...
trichoplax

Sepertinya tidak mungkin saya akan mencapai tujuan itu, saya masih belum berhasil menghasilkan 137 elemen set. Saya pikir metode ini mungkin disadap ...
RB

Menarik bahwa dua jawaban berbeda dengan pendekatan berbeda mencapai maksimum pada ukuran yang sama.
trichoplax

Saya pikir batas atas mungkin cukup dekat. Pertimbangkan bidang yang tak terbatas dan dua titik. Penempatan optimal dari titik-titik tersebut dengan jarak berapa pun dmeminimalkan jumlah titik lain yang dikecualikan dari pertimbangan dengan menelusuri lingkaran jari d- jari dengan pusat dari kedua titik yang dipilih, di mana perimeter hanya menyentuh tiga koordinat bilangan bulat lainnya (pada putaran 90, 180, dan 270 derajat sekitar lingkaran), dan garis membagi dua tegak lurus yang melintasi tidak ada koordinat bilangan bulat. Jadi setiap poin baru n+1akan mengecualikan 6npoin lain dari pertimbangan (dengan pilihan optimal).
BPR

3

Python 3, skor 129

Ini adalah contoh jawaban untuk memulai sesuatu.

Hanya pendekatan naif melalui piksel secara berurutan dan memilih piksel pertama yang tidak menyebabkan jarak pemisahan duplikat, sampai piksel habis.

Kode

width = 619
height = 619
area = width * height
currentAttempt = 0

temporaryLengths = []
lengths = []
points = []
pixels = []
for i in range(area):
    pixels.append(0)


def generate_points():
    global lengths
    while True:
        candidate = vacantPixel()
        if isUnique(candidate):
            lengths += temporaryLengths
            pixels[candidate] = 1
            points.append(candidate)
            print(candidate)
        if currentAttempt == area:
            break
    filename = 'uniquely-separated-points.txt'
    with open(filename, 'w') as file:
        file.write(' '.join(points))


def isUnique(n):
    x = n % width
    y = int(n / width)
    temporaryLengths[:] = []
    for i in range(len(points)):
        point = points[i]
        a = point % width
        b = int(point / width)
        d = distance(x, y, a, b)
        if d in lengths or d in temporaryLengths: 
            return False
        temporaryLengths.append(d)
    return True


def distance(x1, y1, x2, y2):
    xd = x2 - x1
    yd = y2 - y1
    return (xd*xd + yd*yd) ** 0.5


def vacantPixel():
    global currentAttempt
    while True:
        n = currentAttempt
        currentAttempt += 1
        if pixels[n] == 0:
            break
    return n


generate_points()

Keluaran

0 1 3 7 12 20 30 44 65 80 96 122 147 181 203 251 289 360 400 474 564 592 627 660 747 890 1002 1155 1289 1417 1701 1789 1895 2101 2162 2560 2609 3085 3121 3331 3607 4009 4084 4242 4495 5374 5695 6424 6762 6808 7250 8026 8356 9001 9694 10098 11625 12881 13730 14778 15321 16091 16498 18507 19744 20163 20895 23179 25336 27397 31366 32512 33415 33949 39242 41075 46730 47394 48377 59911 61256 66285 69786 73684 79197 89530 95447 102317 107717 111751 116167 123198 126807 130541 149163 149885 154285 159655 163397 173667 173872 176305 189079 195987 206740 209329 214653 220911 230561 240814 249310 269071 274262 276855 285295 305962 306385 306515 312310 314505 324368 328071 348061 350671 351971 354092 361387 369933 376153

Gambar dari Stack Snippet

gambar 129 piksel yang dipisahkan secara unik


3

Python 3, 130

Sebagai perbandingan, berikut ini adalah implementasi backtracker rekursif:

N = 619

def norm(p1, p2):
    return (p1//N - p2//N)**2 + (p1%N - p2%N)**2

def solve(selected, dists):
    global best

    if len(selected) > best:
        print(len(selected), "|", *selected)
        best = len(selected)

    for pixel in (range(selected[-1]+1, N*N) if selected else range((N+1)//2+1)):
        # By symmetry, place first pixel in first half of top row
        added_dists = [norm(pixel, p) for p in selected]
        added_set = set(added_dists)

        if len(added_set) != len(added_dists) or added_set & dists:
            continue

        selected.append(pixel)
        dists |= added_set

        solve(selected, dists)

        selected.pop()
        dists -= added_set

print("N =", N)
best = 0
selected = []
dists = set()
solve(selected, dists)

Ia menemukan solusi 130 piksel berikut dengan cepat sebelum mulai tersedak:

0 1 3 7 12 20 30 44 65 80 96 122 147 181 203 251 289 360 400 474 564 592 627 660 747 890 1002 1155 1289 1417 1701 1789 1895 2101 2162 2560 2609 3085 3121 3331 3607 4009 4084 4242 4495 5374 5695 6424 6762 6808 7250 8026 8356 9001 9694 10098 11625 12881 13730 14778 15321 16091 16498 18507 19744 20163 20895 23179 25336 27397 31366 32512 33415 33949 39242 41075 46730 47394 48377 59911 61256 66285 69786 73684 79197 89530 95447 102317 107717 111751 116167 123198 126807 130541 149163 149885 154285 159655 163397 173667 173872 176305 189079 195987 206740 209329 214653 220911 230561 240814 249310 269071 274262 276855 285295 305962 306385 306515 312310 314505 324368 328071 348061 350671 351971 354092 361387 371800 376153 378169

Lebih penting lagi, saya menggunakannya untuk memeriksa solusi untuk kasus-kasus kecil. Untuk N <= 8, yang optimal adalah:

1: 1 (0)
2: 2 (0 1)
3: 3 (0 1 5)
4: 4 (0 1 6 12)
5: 5 (0 1 4 11 23)
6: 6 (0 1 9 23 32 35)
7: 7 (0 2 9 20 21 40 48)
8: 7 (0 1 3 12 22 56 61)
9: 8 (0 1 3 8 15 37 62 77)
10: 9 (0 1 7 12 30 53 69 80 89)

Terdaftar dalam kurung adalah optimis leksikografis pertama.

Belum dikonfirmasi:

11: 10 (0 2 3 7 21 59 66 95 107 120)
12: 10 (0 1 3 7 33 44 78 121 130 140)

3

Scala, 132

Memindai dari kiri ke kanan dan dari atas ke bawah seperti solusi naif, tetapi coba mulai dari lokasi piksel yang berbeda.

import math.pow
import math.sqrt

val height, width = 619
val area = height * width

case class Point(x: Int, y: Int)

def generate(n: Int): Set[Point] = {

  def distance(p: Point, q: Point) = {
    def square(x: Int) = x * x
    sqrt(square(q.x - p.x) + square(q.y - p.y))
  }

  def hasDuplicates(s: Seq[_]) = s.toSet.size != s.size

  def rotate(s: Vector[Point]): Vector[Point] = s.drop(n) ++ s.take(n)

  val remaining: Vector[Point] =
    rotate((for (y <- 0 until height; x <- 0 until width) yield { Point(x, y) }).toVector)
  var unique = Set.empty[Point]
  var distances = Set.empty[Double]
  for (candidate <- remaining) {
    if (!unique.exists(p => distances.contains(distance(candidate, p)))) {
      val candidateDistances = unique.toSeq.map(p => distance(candidate, p))
      if (!hasDuplicates(candidateDistances)) {
        unique = unique + candidate
        distances = distances ++ candidateDistances
      }
    }
  }
  unique
}

def print(s: Set[Point]) = {
  def toRowMajor(p: Point) = p.y*height + p.x
  println(bestPixels.map(toRowMajor).toSeq.sorted.mkString(" "))
}

var bestPixels = Set.empty[Point]
for (n <- 0 until area) {                                                                                                                                                                                          
  val pixels = generate(n)
  if (pixels.size > bestPixels.size) bestPixels = pixels
}
print(bestPixels)

Keluaran

302 303 305 309 314 322 332 346 367 382 398 424 449 483 505 553 591 619 647 680 719 813 862 945 1014 1247 1459 1700 1740 1811 1861 1979 2301 2511 2681 2913 3114 3262 3368 4253 4483 4608 4753 5202 5522 5760 6246 6474 6579 6795 7498 8062 8573 8664 9903 10023 10567 10790 11136 12000 14153 15908 17314 17507 19331 20563 20941 22339 25131 26454 28475 31656 38328 39226 40214 50838 53240 56316 60690 61745 62374 68522 71208 78598 80204 86005 89218 93388 101623 112924 115702 118324 123874 132852 136186 139775 144948 154274 159730 182200 193642 203150 203616 213145 214149 218519 219744 226729 240795 243327 261196 262036 271094 278680 282306 289651 303297 311298 315371 318124 321962 330614 336472 343091 346698 354881 359476 361983 366972 369552 380486 382491

3
Baru saja bola bergulir ...
Dave Swartz

3

Python, 134 132

Inilah yang sederhana yang secara acak memilah beberapa ruang pencarian untuk mencakup area yang lebih luas. Itu iterates poin dalam jarak dari urutan asal. Itu melompati poin yang jaraknya sama dari asal, dan keluar awal jika tidak bisa meningkatkan yang terbaik. Ini berjalan tanpa batas.

from random import *
from bisect import *

W = H = 619
pts = []
deepest = 0
lengths = set()

def place(x, y):
    global lengths
    pos = (x, y)
    for px, py in pts:
        dist = (x-px)*(x-px) + (y-py)*(y-py)
        if dist in lengths:
            return False
    dists = set((x-px)*(x-px) + (y-py)*(y-py) for px, py in pts)
    if len(dists) != len(pts):
        return False
    lengths |= dists
    pts.append(pos)
    return True

def unplace():
    x, y = pos = pts.pop()
    for px, py in pts:
        dist = (x-px)*(x-px) + (y-py)*(y-py)
        lengths.remove(dist)

def walk(i):
    global deepest, backtrack
    depth = len(pts)
    while i < W*H:
        d, x, y, rem = order[i]
        if rem+depth <= deepest: # early out if remaining unique distances mean we can't improve
            return
        i += 1
        if place(x, y):
            j = i
            while j < W*H and order[j][0] == d: # skip those the same distance from origin
                j += 1
            walk(j)
            unplace()
            if backtrack <= depth:
                break
            if not randint(0, 5): # time to give up and explore elsewhere?
                backtrack = randint(0, len(pts))
                break
            backtrack = W*H # remove restriction
    if depth >= deepest:
        deepest = depth
        print (ox, oy), depth, "=", " ".join(str(y*W+x) for x, y in pts)

try:
    primes = (0,1,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97)
    while True:
        backtrack = W*H
        ox, oy = choice(primes), choice(primes) # random origin coordinates
        order = sorted((float((ox-x)**2+(oy-y)**2)+random(), x, y) for x in xrange(W) for y in xrange(H))
        rem = sorted(set(int(o[0]) for o in order)) # ordered list of unique distances
        rem = {r: len(rem)-bisect_right(rem, r) for r in rem} # for each unique distance, how many remain?
        order = tuple((int(d), x, y, rem[int(d)]) for i, (d, x, y) in enumerate(order))
        walk(0)
except KeyboardInterrupt:
    print

Dengan cepat menemukan solusi dengan 134 poin:

3097 3098 2477 4333 3101 5576 1247 9 8666 8058 12381 1257 6209 15488 6837 21674 19212 26000 24783 1281 29728 33436 6863 37767 26665 14297 4402 43363 50144 52624 18651 9996 58840 42792 6495730730303030303073073030307303030303030303030303030303030303030303030303030303030303073030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030730303030303030303030303030303030303030 Dengan Penawaran Telepon 11.313. 249115 21544 95185 231226 54354 104483 280665 518 147181 318363 1793 248609 82260 52568 365227 361603 346849 331462 69310 90988 341446 229599 277828 382837 339014 323612 365040 269883 317

Bagi yang penasaran, berikut adalah beberapa N kecil paksa:

3  =  0  2  3
4  =  0  2  4  7
5  =  0  2  5 17 23
6  =  0 12 21 28 29 30
7  =  4  6 11 14 27 36 42
8  =  0  2  8 11 42 55 56
9  =  0  2  9 12 26 50 63 71
10 =  0  2  7 10 35 75 86 89  93
11 =  0 23 31 65 66 75 77 95 114 117

Sudahkah Anda mencoba menjalankan ini melalui PyPy ?
trichoplax

1
@trichoplax Saya selalu menjalankan hal-hal hobi ini di kedua pypy dan cpython, dan jika cpython lebih cepat saya mengajukan tiket pada pypy. Dalam kasus khusus ini, pypy sedikit lebih cepat daripada cpython dan itulah cara saya mendapatkan angka-angka ini :)
Will

Saya tertarik, apa artinya "cepat"?
Kain

@Cain 'cepat' sekitar 5 menit iirc
Will

2

Fantom 96

Saya menggunakan algoritma evolusi, pada dasarnya menambahkan titik acak k pada suatu waktu, melakukannya untuk j set acak yang berbeda, lalu memilih yang terbaik dan ulangi. Jawaban yang cukup mengerikan saat ini, tetapi itu dijalankan dengan hanya 2 anak per generasi demi kecepatan, yang hampir hanya acak. Akan bermain dengan parameter sedikit untuk melihat bagaimana kelanjutannya, dan saya mungkin perlu fungsi penilaian yang lebih baik daripada jumlah tempat gratis yang tersisa.

class Pixel
{
  static const Int n := 619
  static const Int stepSize := 20
  static const Int generationSize := 5
  static const |Int, Int -> Int| d := |Int x, Int y -> Int| {
      d1 := x%n - y%n
      d2 := x/n - y/n
      return d1.pow(2) + d2.pow(2)
    }


  public static Void main(){

    //Initialize

    [Int: Int[][]] disMap := [:]
    Int[] freeSpots := (0..<n*n).toList
    Int[] pixels := [,]
    Int[] distances := [,]





    genNum := 0
    children := [,]
    while(freeSpots.size > 0){
      echo("Generation: ${genNum++} \t Spots Left: ${freeSpots.size} \t Pixels added: $pixels.size \t Distances used: $distances.size uniqueDistances: $distances.unique.size" )
      echo(distances)
      echo("Pixels: " + pixels.join(" "))
      //echo("Distances: $distances")
      //Generate children
      children = [,]
      generationSize.times{
        //echo("\tStarting child $it")
        i := Int.random(0..<freeSpots.size)
        childFreeSpots := freeSpots.dup
        childPixels := pixels.dup
        childDistances := distances.dup

        for(Int step := 0; step < stepSize; step++){

          if( i < childFreeSpots.size){
            //Choose a pixel
            pixel := childFreeSpots.removeAt(i)
            //echo("\t\tAdding pixel $pixel")

            //Remove neighbors that are the new distances away
            ///Find distances
            newDis := [,]
            childPixels.each { 
              newDis.add(d(pixel, it))
            }

            //Check that there are no equal distances
            if(newDis.size != newDis.unique.size) continue



            //Remove neighbors
            childPixels.each | Int childPixel|{
              newDis.each |Int dis|{
                neighbors := getNeighbors(childPixel, dis, disMap)
                neighbors.each| Int n |{
                  index := childFreeSpots.binarySearch(n)
                  if(index >= 0) childFreeSpots.removeAt(index)
                }
              }
            }
            //echo("Removed neighbors: $test")
            //Remove all the neighbors of new pixel
            childDistances.addAll(newDis)
            childDistances.each|Int dis| {   
              neighbors := getNeighbors(pixel, dis, disMap)
              childFreeSpots.removeAll(neighbors)
            }

            //Add new pixel
            childPixels.add(pixel)  
          }
        }
        children.add([childPixels.dup, childDistances.dup, childFreeSpots.dup])
        echo("\tChild $it: pixels: $childPixels.size \t distances: $childDistances.size \t freeSpots: $childFreeSpots.size")
      }

      //Score children and keep best one as new parent
      Obj?[][] parent := children.max |Int[][] a, Int[][] b -> Int| { return (a.last.size  + a.first.size*10000) <=> (b.last.size + b.first.size*10000)  }
      pixels = parent.first
      distances = parent[1]
      freeSpots = parent.last

    }//End while


    //Return result
    echo("Size: " + pixels.size)
    echo(pixels.join(" "))





  }

  private static Bool checkValid(Int[] pixels){
    distances := [,]
    pixels[0..-2].each|Int p, Int i|{
      for(Int j := i + 1; j < pixels.size; j++){
        distances.add(d(p, pixels[j]))
      }
    }
    if(distances.size > distances.unique.size){
      echo("Duplicate distance found!!!!")
      echo("Pixel $pixels.last is not valid")
      return false
    }
    return true
  }

  public static Int[] getNeighbors(Int spot, Int distance, [Int : Int[][]] disMap ){
    result := [,]
    //Check hash map
    pairs := disMap.get(distance, null)

    //Find possible int pairs if not already in the map
    if(pairs == null){
      for(Int i := 0; i*i <= distance; i++ ){
        for(Int j := i; j*j + i*i <= distance; j++){
          if(i.pow(2) + j.pow(2) == distance){
            pairs.add([i, j])
          }
        }
      }
      disMap.add(distance, pairs)
    }

    pairs.each|Int[] pair|{
      //Find neighbors with pair
      x := pair.first
      y := pair.last
      2.times{ 
        //Positive x
        result.add(spot + x + y*n)
        result.add(spot + x - y*n)

        //negative x
        result.add(spot - x + y*n)
        result.add(spot - x - y*n)

        //Swap x and y and repeat
        temp := x
        x = y
        y = temp
      }
    }

    return result.findAll |Int i -> Bool| { i >= 0 }.unique
  }

}

Keluaran

17595 17596 17601 17627 17670 17726 17778 17861 17956 18117 18324 18733 19145 19597 20244 21139 21857 22742 24078 25343 28577 30152 32027 34406 37008 39864 42313 44820 48049 52193 55496 59707 64551 69976 74152 79758 84392 91782 98996 104625 150212 158877 169579 178660 189201 201343 213643 225998 238177 251012 263553 276797 290790 304915 319247 332702 347266 359665 373683 125899 144678 170677 195503 220092 244336 269861 289473 308633 326736 343756 358781 374280 131880 172485 212011 245015 277131 302055 321747 347911 363717 379166 249798 284200 313870 331913 360712 378024 9704 141872 249686 293656 357038 357596 370392 381963

1
Oh wow, kamu benar, aku minta maaf. Hmm, pasti belum menyalin semua itu lebih awal ketika saya diuji. Saya akan memperbaiki apa pun yang terjadi dan merespons dengan pembaruan
Kain

Ahh, saya menemukan jawabannya, ketika menambahkan piksel baru, saya tidak memeriksa bahwa itu tidak sama dari dua piksel lainnya
Kain

Memperbaikinya, tapi itu benar-benar menyebalkan sekarang, saya pikir saya mungkin secara tidak sengaja menemukan solusi terburuk daripada yang terbaik
Kain

Setidaknya itu berfungsi sekarang, sehingga Anda dapat mengubah parameter dan melihat apakah Anda dapat meningkatkan hasilnya. Luar biasa melihat pendekatan baru lainnya. +1
trichoplax

1

Python 3, 119

Saya tidak lagi ingat mengapa saya menamai fungsi ini mc_usp, meskipun saya curiga ada hubungannya dengan rantai Markov. Di sini saya menerbitkan kode saya yang saya jalankan dengan PyPy selama sekitar 7 jam. Program ini mencoba untuk membangun 100 set piksel yang berbeda dengan memilih piksel secara acak hingga memeriksa setiap piksel dalam gambar, dan mengembalikan salah satu set terbaik.

Pada catatan lain, pada titik tertentu, kita harus benar-benar berusaha untuk menemukan batas atas N=619yang lebih baik daripada 488, karena menilai dari jawaban di sini, angka itu terlalu tinggi. Komentar Rowan Blush tentang bagaimana setiap poin baru n+1berpotensi menghilangkan 6*npoin dengan pilihan optimal sepertinya ide yang bagus. Sayangnya, setelah memeriksa formula a(1) = 1; a(n+1) = a(n) + 6*n + 1, di mana a(n)jumlah poin dihapus setelah menambahkan npoin ke set kami, ide ini mungkin tidak paling cocok. Memeriksa kapan a(n)lebih besar dari N**2, a(200)lebih besar dari yang 619**2tampaknya menjanjikan, tetapi a(n)lebih besar dari 10**2itu a(7)dan kami telah membuktikan bahwa 9 adalah batas atas aktual untukN=10. Saya akan membuat Anda diposting karena saya mencoba untuk melihat batas atas yang lebih baik, tetapi ada saran dipersilahkan.

Ke jawaban saya. Pertama, set 119 piksel saya.

15092 27213 294010 340676 353925 187345 127347 21039 28187 4607 23476 324112 375223 174798 246025 185935 186668 138651 273347 318338 175447 316166 158342 97442 361309 251283 29986 98029 339602 292202 304041 353401 236737 324696 42096 102574 357602 66845 40159 57866 3291 24583 254208 357748 304592 86863 19270 228963 87315 355845 55101 282039 83682 55643 292167 268632 118162 48494 378303 128634 117583 841 178939 20941 161231 247142 110205 211040 90946 170124 362592 327093 336321 291050 29880 279825 212675 138043 344012 187576 168354 28193 331713 329875 321927 129452 163450 1949 186448 50734 14422 3761 322400 318075 77824 36391 31016 33491 360713 352240 45316 79905 376004 310778 382640 383077 359178 14245 275451 362125 268047 23437 239772 299047 294065 46335 112345 382617 79986

Kedua, kode saya, yang secara acak mengambil titik awal dari oktan 619x619 persegi (karena titik awal dinyatakan sama di bawah rotasi dan refleksi) dan kemudian setiap titik lainnya dari sisa kotak.

import random
import time

start_time = time.time()
print(start_time)

def mc_usp_v3(N, z, k=100, m=1.0):
    """
    At m=1.0, it keeps randomly picking points until we've checked every point. Oh dear.
    """
    ceil = -(-N//2)
    a=random.randint(0,ceil)
    b=random.randint(a,ceil)
    r=[a*N+b]

    best_overall = r[:]
    all_best = []
    best_in_shuffle = r[:]
    num_shuffles = 0
    num_missteps = 0
    len_best = 1

    while num_shuffles < k and len(best_overall) < z:
        dist = []
        missteps = []
        points_left = list(range(N*N))
        points_left.remove(r[0])

        while len_best + num_missteps < m*N*N and len(points_left):
            index = random.randint(0, len(points_left)-1)
            point = points_left[index]
            points_left.pop(index)
            dist, better = euclid(r, point, dist, N)

            if better and len(r) + 1 > len_best:
                r.append(point)
                best_in_shuffle = r[:]
                len_best += 1
            else:
                missteps.append(point)
                num_missteps += 1

        else:
            print(num_shuffles, len(best_overall), len_best, num_missteps, time.time() - start_time)

            num_shuffles += 1
            num_missteps = 0
            missteps = []

            if len(best_in_shuffle) == len(best_overall):
                all_best.append(best_in_shuffle)
                print(best_in_shuffle)

            if len(best_in_shuffle) > len(best_overall):
                best_overall = best_in_shuffle[:]
                all_best = [best_overall]
                print(best_overall)
            a=random.randint(0,ceil)
            b=random.randint(a,ceil)
            r=[a*N+b]
            best_in_shuffle = r[:]
            len_best = 1
    return len(best_overall), all_best

def euclid(point_set, new_point, dist, N):
    new_dist = []
    unique = True
    a,b=divmod(new_point, N)
    for point in point_set:
        c,d=divmod(point, N)
        current_dist = (a-c)**2+(b-d)**2
        if current_dist in dist or current_dist in new_dist:
            unique = False
            break
        new_dist.append(current_dist)
    if unique:
        dist += new_dist
    return dist, unique

def mcusp_format(mcusp_results):
    length, all_best = mcusp_results
    return " ".join(str(i) for i in all_best[0])

print(mcusp_format(mc_usp_v3(10, 20, 100, 1.0)))
print(mcusp_format(mc_usp_v3(619, 488, 100, 1.0)))
print(time.time()-start_time)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.