Thử thách mã ngôi sao


21

Quốc kỳ của Hoa Kỳ chứa, trong bang của nó, 50 ngôi sao, đại diện cho 50 tiểu bang.

Cờ Mỹ 50 sao

Trong quá khứ, khi có ít tiểu bang hơn, tất nhiên có ít ngôi sao hơn và chúng được sắp xếp khác nhau. Ví dụ, từ 1912-1959 (sau khi nhập học New Mexico và Arizona nhưng trước Alaska), đã có 48 ngôi sao trong một sắp xếp hình chữ nhật 6 × 8.

Cờ Mỹ 48 sao

Cờ 37 sao được sử dụng từ năm 1867-1877 (sau khi nhập học Nebraska nhưng trước Colorado) có mô hình ngôi sao không đối xứng.

Cờ Mỹ 37 sao

Trong trường hợp một tiểu bang thứ 51 được thêm vào trong tương lai, Học viện Quân đội đã phát triển một thiết kế sơ bộ cho một lá cờ mới.

Cờ Mỹ 51 sao

Nhưng không có thuật toán chung để sắp xếp các ngôi sao, vì vậy hãy tạo ra một!

Các thách thức

Viết chương trình cho một số ngôi sao nhất định đặt vào bang (phần màu xanh) của cờ Hoa Kỳ, đưa ra tọa độ tối ưu để đặt các ngôi sao đó. Hệ tọa độ được xác định bằng tổng [ không là toàn bộ cờ] với 0≤x≤W và 0≤y≤H.

Với mục đích của thử thách này, một sự sắp xếp tối ưu hóa được định nghĩa là một tối thiểu hóa khoảng cách trung bình (Euclide) giữa một điểm trong bang và trung tâm của ngôi sao gần nhất.

Một thuật toán đơn giản (nếu có thể là tối ưu) để xấp xỉ giá trị này là:

def mean_distance_to_nearest_star(stars, width, height, point_density=100):
   """
   Approximate the mean distance between a point in the rectangle
   0 < x < width and 0 < y < height, and the nearest point in stars.

   stars -- list of (x, y) points
   width, height -- dimensions of the canton
   """
   total = 0.0
   nx = round(width * point_density)
   ny = round(height * point_density)
   for ix in range(nx):
       x = (ix + 0.5) * width / nx
       for iy in range(ny):
          y = (iy + 0.5) * width / ny
          min_dist = float('inf')
          for sx, sy in stars:
              min_dist = min(min_dist, math.hypot(x - sx, y - sy))
          total += min_dist
   return total / (nx * ny)

Chương trình của bạn sẽ có ba đối số dòng lệnh (không tính tên chương trình):

  1. Số lượng ngôi sao để đặt trong bang.
  2. Chiều rộng của bang. (Phải chấp nhận giá trị dấu phẩy động.)
  3. Chiều cao của bang. (Phải chấp nhận giá trị dấu phẩy động.)

(Nếu ngôn ngữ lập trình ưa thích của bạn không hỗ trợ các đối số dòng lệnh, hãy làm một cái gì đó tương đương hợp lý và ghi lại nó trong câu trả lời của bạn.)

Đầu ra phải bao gồm các giá trị X và Y được phân tách bằng dấu phẩy, từ một đến một dòng. (Thứ tự các điểm không quan trọng.)

Ví dụ:

~$ flagstar 5 1.4 1.0
0.20,0.20
0.20,0.80
0.70,0.50
1.20,0.20
1.20,0.80

Quy tắc bổ sung & ghi chú

  • Tôi có quyền đóng lại sơ hở trong các quy tắc bất cứ lúc nào.
  • Hạn chót cho câu trả lời là Thứ Sáu, ngày 4 tháng 7 lúc 24:00 CDT (UTC-05: 00). Do thiếu câu trả lời, thời hạn đã được gia hạn. TBA.
  • Bao gồm trong câu trả lời của bạn:
    • Mã chương trình của bạn
    • Một lời giải thích về cách thức hoạt động của nó
    • Đầu ra của nó với các đối số dòng lệnh 50 1.4 1.0
  • Chương trình của bạn phải chạy trong một khoảng thời gian hợp lý: Tối đa 5 phút trên một PC thông thường. Tôi sẽ không cực kỳ nghiêm ngặt về vấn đề này, nhưng sẽ loại bỏ chương trình của bạn nếu phải mất hàng giờ .
  • Chương trình của bạn phải có tính xác định, nghĩa là luôn luôn cung cấp chính xác cùng một đầu ra cho cùng một đối số. Vì vậy, đừng phụ thuộc vào time()hoặc rand(). Phương pháp Monte Carlo là OK miễn là bạn cuộn PRNG của riêng bạn.
  • Chỉ có các điểm trung tâm của các ngôi sao quan trọng. Đừng lo lắng về việc cố gắng tránh chồng chéo hoặc bất cứ điều gì như thế.

Chấm điểm

  • Giảm thiểu khoảng cách trung bình từ một điểm trong bang đến ngôi sao gần nhất. (Xem ở trên.)
  • Bạn có thể được ghi điểm dựa trên bất kỳ cờ lịch sử nào của Hoa Kỳ, từ 13 đến 50 sao. Thuật toán chính xác cho điểm số trọng số vào một bảng xếp hạng sẽ được đăng sau.
  • Trong trường hợp hòa, người chiến thắng sẽ được chọn theo số lần nâng cấp ròng.
  • Tôi có thể sẽ đăng một chương trình của riêng tôi, nhưng sẽ loại trừ bản thân tôi không đủ điều kiện cho dấu kiểm.

@primo: Làm thế nào để bạn hình dung điều đó? Ví dụ của tôi có khoảng cách trung bình đến ngôi sao gần nhất là 0,289, trong khi đặt tất cả 5 điểm vào trung tâm có MDNS là 0,561.
dan04

Bạn có thể bỏ qua cam kết trước đây của tôi. Tôi đọc sai khoảng cách trung bình từ mọi điểm trên bang đến ngôi sao gần nhất, như khoảng cách trung bình từ mọi ngôi sao đến ngôi sao gần nhất.
primo

3
Vui lòng bao gồm jsfiddle.net/nf2mk2gr dưới dạng Stack Snippet trong câu hỏi để kiểm tra đầu ra của câu trả lời, nếu nó đáp ứng sự chấp thuận của bạn. Nó hiển thị khoảng cách trung bình dựa trên lưới N theo N, với N tăng dần khi bạn chờ đợi lâu hơn. (Nó được viết riêng cho câu hỏi này.)
trichoplax

Câu trả lời:


4

Javascript - di chuyển các ngôi sao về phía điểm cô lập nhất

(với một hình ảnh động của quá trình)

Cách tiếp cận rất đơn giản:

  • chọn một số lượng lớn các điểm ngẫu nhiên
  • tìm ngôi sao gần nhất với nhau
  • chọn điểm mà ngôi sao gần nhất ở xa nhất
  • di chuyển ngôi sao đó đến gần điểm đó

Quá trình này được lặp đi lặp lại một số lượng lớn, giảm dần số lượng mà các ngôi sao được di chuyển. Điều này làm giảm khoảng cách tối đa từ một điểm đến ngôi sao gần nhất, gián tiếp giảm khoảng cách trung bình từ một điểm đến ngôi sao gần nhất.

Theo yêu cầu của câu hỏi, điều này không sử dụng hàm ngẫu nhiên tích hợp, thay vào đó sử dụng xorshift .

Phần lớn các mã bao gồm thiết lập và hoạt hình - phần áp dụng thuật toán là hàm adjustStars.

Bạn có thể xem quá trình đang diễn ra trong Stack Snippet bên dưới.

stars = [];
timeoutId = 0;

resetRandomNumberGenerator();

function resetRandomNumberGenerator() {
  rng_x = 114; // Numbers for the random number generator.
  rng_y = 342;
  rng_z = 982;
  rng_w = 443;
}

$(document).ready(function() {
  c = document.getElementById('canton');
  ctx = c.getContext('2d');
  resizeCanvas();
});

function stop() {
  clearTimeout(timeoutId);
}

function arrange() {
  clearTimeout(timeoutId);
  resetStars();
  resetRandomNumberGenerator();
  maxStepSize = Math.min(cantonWidth, cantonHeight) / 4;
  adjustStars(maxStepSize, 8000, 10000);
}

function resizeCanvas() {
  cantonWidth = parseFloat($('#width').val());
  cantonHeight = parseFloat($('#height').val());
  starRadius = cantonHeight / 20;
  document.getElementById('canton').width = cantonWidth;
  document.getElementById('canton').height = cantonHeight;
  ctx.fillStyle = 'white';
  resetStars();
}

function resetStars() {
  stop();
  stars = [];
  population = parseInt($('#stars').val(), 10);
  shortSide = Math.floor(Math.sqrt(population));
  longSide = Math.ceil(population / shortSide);
  if (cantonWidth < cantonHeight) {
    horizontalStars = shortSide;
    verticalStars = longSide;
  } else {
    horizontalStars = longSide;
    verticalStars = shortSide;
  }
  horizontalSpacing = cantonWidth / horizontalStars;
  verticalSpacing = cantonHeight / verticalStars;
  for (var i = 0; i < population; i++) {
    x = (0.5 + (i % horizontalStars)) * horizontalSpacing;
    y = (0.5 + Math.floor(i / horizontalStars)) * verticalSpacing;
    stars.push([x, y]);
  }
  drawStars();
  updateOutputText();
}

function adjustStars(stepSize, maxSteps, numberOfPoints) {
  $('#stepsRemaining').text(maxSteps + ' steps remaining');
  points = randomPoints(numberOfPoints);
  mostIsolatedPoint = 0;
  distanceToNearestStar = 0;
  for (var i = 0; i < numberOfPoints; i++) {
    point = points[i];
    x = point[0];
    y = point[1];
    star = stars[nearestStar(x, y)];
    d = distance(x, y, star[0], star[1]);
    if (d > distanceToNearestStar) {
      distanceToNearestStar = d;
      mostIsolatedPoint = i;
    }
  }
  point = points[mostIsolatedPoint];
  x = point[0];
  y = point[1];

  starToMove = nearestStar(x, y);

  star = stars[starToMove];
  separationX = x - star[0];
  separationY = y - star[1];
  if (separationX || separationY) {
    hypotenuse = distance(x, y, star[0], star[1]);
    currentStep = Math.min(stepSize, hypotenuse / 2);
    deltaX = currentStep * separationX / hypotenuse;
    deltaY = currentStep * separationY / hypotenuse;
    star[0] += deltaX;
    star[1] += deltaY;
    if (star[0] < 0) star[0] = 0;
    if (star[0] > cantonWidth) star[0] = cantonWidth;
    if (star[1] < 0) star[1] = 0;
    if (star[1] > cantonHeight) star[1] = cantonHeight;

    drawStars();
    updateOutputText();
  }

  if (maxSteps > 0) {
    timeoutId = setTimeout(adjustStars, 10, stepSize * 0.9992, maxSteps - 1, numberOfPoints);
  }
}

function updateOutputText() {
  starText = '';
  for (var i = 0; i < stars.length; i++) {
    starText += stars[i][0] + ', ' + stars[i][1] + '\n';
  }
  $('#coordinateList').text(starText);
}

function randomPoints(n) {
  pointsToReturn = [];
  for (i = 0; i < n; i++) {
    x = xorshift() * cantonWidth;
    y = xorshift() * cantonHeight;
    pointsToReturn.push([x, y]);
  }
  return pointsToReturn;
}

function xorshift() {
  rng_t = rng_x ^ (rng_x << 11);
  rng_x = rng_y;
  rng_y = rng_z;
  rng_z = rng_w;
  rng_w = rng_w ^ (rng_w >> 19) ^ rng_t ^ (rng_t >> 8);
  result = rng_w / 2147483648
  return result
}

function nearestStar(x, y) {
  var distances = [];
  for (var i = 0; i < stars.length; i++) {
    star = stars[i];
    distances.push(distance(x, y, star[0], star[1]));
  }
  minimum = Infinity;
  for (i = 0; i < distances.length; i++) {
    if (distances[i] < minimum) {
      minimum = distances[i];
      nearest = i;
    }
  }
  return nearest;
}

function distance(x1, y1, x2, y2) {
  var x = x2 - x1;
  var y = y2 - y1;
  return Math.sqrt(x * x + y * y);
}

function drawStars() {
  ctx.clearRect(0, 0, cantonWidth, cantonHeight);
  for (i = 0; i < stars.length; i++) {
    star = stars[i];
    x = star[0];
    y = star[1];
    drawStar(x, y);
  }
}

function drawStar(x, y) {
  ctx.beginPath();
  ctx.moveTo(x, y - starRadius);
  ctx.lineTo(x - 0.588 * starRadius, y + 0.809 * starRadius);
  ctx.lineTo(x + 0.951 * starRadius, y - 0.309 * starRadius);
  ctx.lineTo(x - 0.951 * starRadius, y - 0.309 * starRadius);
  ctx.lineTo(x + 0.588 * starRadius, y + 0.809 * starRadius);
  ctx.fill();
}
canvas {
  margin: 0;
  border: medium solid gray;
  background-color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<input id='stars' onchange='resetStars()' type='number' value='13' min='13' max='50' maxlength='2' step='1'>stars
<br>
<input id='width' onchange='resizeCanvas()' type='number' value='494' min='1' max='500' maxlength='3' step='any'>width
<br>
<input id='height' onchange='resizeCanvas()' type='number' value='350' min='1' max='500' maxlength='3' step='any'>height
<br>
<button type='button' onclick='resetStars()'>Reset Stars</button>
<button type='button' onclick='arrange()'>Arrange Stars</button>
<button type='button' onclick='stop()'>Stop</button>
<textarea id='stepsRemaining' rows='1' readonly></textarea>
<br>
<canvas id='canton' width='494' height='350'></canvas>
<br>
<textarea id='coordinateList' rows='50' cols='40' readonly></textarea>

Đầu ra cho 50 sao

(chiều rộng = 1,4, chiều cao = 1,0)

Khoảng cách trung bình ước tính là 0,0655106697162357.

Tọa độ:

0.028377044205135808, 0.2128159150679491
0.10116766857540277, 0.05156676609341312
0.2903566419069437, 0.07216263690037035
0.49154061258041604, 0.004436102736309105
0.6930026352073071, 0.07060477929576484
1.0988644764108417, 0.022979778480838074
1.1735677936511582, 0.18600858289592742
1.3056806950504931, 0.062239869036660435
0.3967626880807638, 0.24483447327177033
0.27004118129346155, 0.40467589936498805
0.4996665039421278, 0.13023282430440133
0.5148978532656602, 0.6161298793146592
0.5907056537744844, 0.2614323599301046
0.8853042432872087, 0.048123917861564044
0.7753680330575412, 0.22938793622044834
1.365432954694329, 0.2355377720528128
0.1985172068244217, 0.23551298706793927
0.4477558465270544, 0.4170264112485973
0.6084424566752479, 0.7764909501169484
0.6099528761580699, 0.4395002434593519
0.9506038166406011, 0.34903243854585914
1.1898331497634231, 0.5756784243472182
1.0933574395540542, 0.46422120794648786
1.1516574254138159, 0.2930213338333888
0.07646053006349718, 0.40665000611360175
0.0634456093015551, 0.5853189455014883
0.3470036636019768, 0.5938838331082922
0.7591083341283029, 0.4005456925638841
0.9745306853981277, 0.184624209972443
1.3552011948311598, 0.549607060691302
1.3334000268566828, 0.7410204535471169
1.2990417572304487, 0.39571229988825735
0.05853941030364222, 0.7734808757471414
0.19396697551982484, 0.5678753467094985
0.7103231124251072, 0.5955041661956884
0.6168410756137566, 0.948561537739087
0.8967624790188228, 0.5368666961690878
0.9751229155529001, 0.8323724819557795
0.9987127931392165, 0.652902038374714
1.3231032600971289, 0.9164326184290812
0.20785221980162555, 0.7566700629874374
0.3987967842137651, 0.7678025218448816
0.44395949605458546, 0.9137553802571048
0.775611700149756, 0.9029717946067138
0.806442448003616, 0.7328147396477286
0.9481952441521928, 0.9872963855418118
1.1528689317425114, 0.9346775634274639
1.1651295140721658, 0.7591158327925681
0.09316709042512515, 0.934205211493484
0.2769325337580081, 0.9341145493466471

Sau khi chạy hoạt hình của bạn với nhiều số sao khác nhau, có vẻ như nó có xu hướng đặt các ngôi sao gần các cạnh của hộp. Tuy nhiên, không biết sự sắp xếp tối ưu thực sự, tôi không thể biết đây là lỗi hay là một tính năng.
dan04

@ dan04 cũng không phải tôi - Tôi có một ý tưởng về lý do tại sao nó xảy ra mặc dù. Các ngôi sao gần rìa quá gần nó sẽ có xác suất đáng kể chúng di chuyển về phía nó (các ngôi sao chủ yếu di chuyển về phía các điểm cách ly nhất, không phải các điểm gần đó). Nhưng chúng vẫn có thể di chuyển về phía rìa một cách gián tiếp, bằng cách xen kẽ giữa việc di chuyển về phía hai điểm xa gần rìa, dẫn đến một con đường ngoằn ngoèo. Tôi nghi ngờ điều này có nghĩa cần phải có các ngôi sao ở gần rìa, nhưng tôi mong muốn được thấy một cách tiếp cận khác để xem liệu điều đó có chia sẻ lỗi / tính năng không ...
trichoplax

@ dan04 Câu trả lời thứ hai của tôi dường như cho thấy rằng các ngôi sao không cần phải ở gần các cạnh như tôi nghĩ, và cho kết quả tốt hơn câu trả lời đầu tiên của tôi. Làm việc trực tiếp với giá trị trung bình thay vì gián tiếp thông qua mức tối đa hóa ra có hiệu quả hơn.
trichoplax

3

Đây là một ví dụ đơn giản. Nó luôn sắp xếp các ngôi sao thành một lưới hình chữ nhật và tối ưu hóa nó bằng cách chọn hệ số trong đó các ô lưới càng gần hình vuông càng tốt. Nó hoạt động rất tốt khi số lượng sao có ước số gần với căn bậc hai của nó và bi quan khi số sao là số nguyên tố.

from __future__ import division
import math
import sys

def divisors(n):
    """
    Return all divisors of n (including n itself) as a set.
    """
    result = {1, n}
    # Use +2 instead of +1 to allow for floating-point error.
    for i in range(2, int(math.sqrt(n)) + 2):
        if n % i == 0:
            result.add(i)
            result.add(n // i)
    return result

def squareness(width, height):
    """
    Given the dimensions of a rectangle, return a value between 0 and 1
    (1 iff width == height) measuring how close it is to being a square.
    """
    if width and height:
        return min(width / height, height / width)
    else:
        return 0.0

def star_grid(num_stars, width, height):
    """
    Return the factors (x, y) of num_stars that optimize the mean
    distance to the nearest star.
    """
    best_squareness = 0.0
    best_dimensions = (None, None)
    for nx in divisors(num_stars):
        ny = num_stars // nx
        sq = squareness(width / nx, height / ny)
        if sq > best_squareness:
            best_squareness = sq
            best_dimensions = (nx, ny)
    return best_dimensions

def star_coords(num_stars, width, height):
    """
    Return a list of (x, y) coordinates for the stars.
    """
    nx, ny = star_grid(num_stars, width, height)
    for ix in range(nx):
        x = (ix + 0.5) * width / nx
        for iy in range(ny):
            y = (iy + 0.5) * height / ny
            yield (x, y)

def _main(argv=sys.argv):
    num_stars = int(argv[1])
    width = float(argv[2])
    height = float(argv[3])
    for coord in star_coords(num_stars, width, height):
        print('%g,%g' % coord)

if __name__ == '__main__':
    _main()

Đầu ra cho 50 sao

(chiều rộng = 1,4, chiều cao = 1,0)

Một hình chữ nhật 10 × 5.

0.07,0.1
0.07,0.3
0.07,0.5
0.07,0.7
0.07,0.9
0.21,0.1
0.21,0.3
0.21,0.5
0.21,0.7
0.21,0.9
0.35,0.1
0.35,0.3
0.35,0.5
0.35,0.7
0.35,0.9
0.49,0.1
0.49,0.3
0.49,0.5
0.49,0.7
0.49,0.9
0.63,0.1
0.63,0.3
0.63,0.5
0.63,0.7
0.63,0.9
0.77,0.1
0.77,0.3
0.77,0.5
0.77,0.7
0.77,0.9
0.91,0.1
0.91,0.3
0.91,0.5
0.91,0.7
0.91,0.9
1.05,0.1
1.05,0.3
1.05,0.5
1.05,0.7
1.05,0.9
1.19,0.1
1.19,0.3
1.19,0.5
1.19,0.7
1.19,0.9
1.33,0.1
1.33,0.3
1.33,0.5
1.33,0.7
1.33,0.9

0

Javascript - di chuyển một ngôi sao ngẫu nhiên nếu khoảng cách trung bình giảm

(với một hình ảnh động của quá trình)

Điều này không mang lại một hình ảnh động bận rộn như câu trả lời đầu tiên của tôi, có thời gian dài không có chuyển động vì sự sắp xếp lại tiềm năng được kiểm tra và từ chối. Tuy nhiên, kết quả cuối cùng có khoảng cách trung bình thấp hơn, vì vậy phương pháp này là một sự cải tiến.

Cách tiếp cận vẫn rất đơn giản:

  • Chọn một ngôi sao ngẫu nhiên
  • Di chuyển nó một khoảng cách ngẫu nhiên theo một hướng ngẫu nhiên
  • Nếu khoảng cách trung bình giảm, giữ vị trí mới

Quá trình này được lặp đi lặp lại một số lượng lớn, giảm dần số lượng mà các ngôi sao được di chuyển. Sự lựa chọn ngẫu nhiên về khoảng cách để di chuyển là thiên về các khoảng cách nhỏ hơn, do đó tiến trình là trong những thay đổi nhỏ xen kẽ với bước nhảy lớn hơn thường xuyên. Mỗi bước mất nhiều thời gian hơn trong câu trả lời đầu tiên của tôi, vì đo khoảng cách trung bình là một quá trình chậm đòi hỏi phải lấy mẫu toàn bộ bang.

Theo yêu cầu của câu hỏi, điều này không sử dụng hàm ngẫu nhiên tích hợp, thay vào đó sử dụng xorshift .

Phần lớn các mã bao gồm thiết lập và hoạt hình - phần áp dụng thuật toán là hàm adjustStars.

Bạn có thể xem quá trình đang diễn ra trong Stack Snippet bên dưới.

stars = [];
timeoutId = 0;

resetRandomNumberGenerator();

function resetRandomNumberGenerator() {
  rng_x = 114; // Numbers for the random number generator.
  rng_y = 342;
  rng_z = 982;
  rng_w = 443;
}

$(document).ready(function() {
  c = document.getElementById('canton');
  ctx = c.getContext('2d');
  resizeCanvas();
});

function stop() {
  clearTimeout(timeoutId);
}

function arrange() {
  clearTimeout(timeoutId);
  resetStars();
  resetRandomNumberGenerator();
  maxStepSize = Math.min(cantonWidth, cantonHeight) / 16;
  adjustStars(maxStepSize, 7000, 15000);
}

function resizeCanvas() {
  cantonWidth = parseFloat($('#width').val());
  cantonHeight = parseFloat($('#height').val());
  starRadius = cantonHeight / 20;
  document.getElementById('canton').width = cantonWidth;
  document.getElementById('canton').height = cantonHeight;
  ctx.fillStyle = 'white';
  resetStars();
}

function resetStars() {
  stop();
  stars = [];
  population = parseInt($('#stars').val(), 10);
  shortSide = Math.floor(Math.sqrt(population));
  longSide = Math.ceil(population / shortSide);
  if (cantonWidth < cantonHeight) {
    horizontalStars = shortSide;
    verticalStars = longSide;
  } else {
    horizontalStars = longSide;
    verticalStars = shortSide;
  }
  horizontalSpacing = cantonWidth / horizontalStars;
  verticalSpacing = cantonHeight / verticalStars;
  for (var i = 0; i < population; i++) {
    x = (0.5 + (i % horizontalStars)) * horizontalSpacing;
    y = (0.5 + Math.floor(i / horizontalStars)) * verticalSpacing;
    stars.push([x, y]);
  }
  drawStars();
  updateOutputText();
}

function adjustStars(stepSize, maxSteps, numberOfPoints) {
  $('#stepsRemaining').text(maxSteps + ' steps remaining');
  var points = randomPoints(numberOfPoints);
  currentMean = meanDistance(stars, points);
  potentialStars = shiftedStars(stepSize);
  potentialMean = meanDistance(potentialStars, points);
  if (potentialMean < currentMean) {
    stars = potentialStars;
  }
  drawStars();
  updateOutputText();
  
  if (maxSteps > 0) {
    timeoutId = setTimeout(adjustStars, 10, stepSize * 0.999, maxSteps - 1, numberOfPoints);
  }
}

function shiftedStars(stepSize) {
  shifted = [];
  chosenOne = Math.floor(xorshift() * stars.length);
  for (i = 0; i < stars.length; i++) {
    star = stars[i];
    x = star[0];
    y = star[1];
    if (i === chosenOne) {
      for (n = 0; n < 10; n++) {
        x += xorshift() * stepSize;
        x -= xorshift() * stepSize;
        y += xorshift() * stepSize;
        y -= xorshift() * stepSize;
      }
      if (x < 0) x = 0;
      if (x > cantonWidth) x = cantonWidth;
      if (y < 0) y = 0;
      if (y > cantonHeight) y = cantonHeight;
    }
    shifted.push([x, y]);
  }
  return shifted;    
}

function meanDistance(arrayOfStars, arrayOfPoints) {
  var totalDistance = 0;
  for (i = 0; i < arrayOfPoints.length; i++) {
    point = arrayOfPoints[i];
    x = point[0];
    y = point[1];
    totalDistance += nearestStarDistance(x, y, arrayOfStars);
  }
  return totalDistance / arrayOfPoints.length;
}

function randomPoints(numberOfPoints) {
  var arrayOfPoints = [];
  for (i = 0; i < numberOfPoints; i++) {
    x = xorshift() * cantonWidth;
    y = xorshift() * cantonHeight;
    arrayOfPoints.push([x, y]);
  }
  return arrayOfPoints;
}

function updateOutputText() {
  starText = '';
  for (var i = 0; i < stars.length; i++) {
    starText += stars[i][0] + ', ' + stars[i][1] + '\n';
  }
  $('#coordinateList').text(starText);
}

function xorshift() {
  rng_t = rng_x ^ (rng_x << 11);
  rng_x = rng_y;
  rng_y = rng_z;
  rng_z = rng_w;
  rng_w = rng_w ^ (rng_w >> 19) ^ rng_t ^ (rng_t >> 8);
  result = rng_w / 2147483648
  return result
}

function nearestStarDistance(x, y, starsToUse) {
  var distances = [];
  for (var i = 0; i < stars.length; i++) {
    star = starsToUse[i];
    distances.push(distance(x, y, star[0], star[1]));
  }
  minimum = Infinity;
  for (i = 0; i < distances.length; i++) {
    if (distances[i] < minimum) {
      minimum = distances[i];
    }
  }
  return minimum;
}

function distance(x1, y1, x2, y2) {
  var x = x2 - x1;
  var y = y2 - y1;
  return Math.sqrt(x * x + y * y);
}

function drawStars() {
  ctx.clearRect(0, 0, cantonWidth, cantonHeight);
  for (i = 0; i < stars.length; i++) {
    star = stars[i];
    x = star[0];
    y = star[1];
    drawStar(x, y);
  }
}

function drawStar(x, y) {
  ctx.beginPath();
  ctx.moveTo(x, y - starRadius);
  ctx.lineTo(x - 0.588 * starRadius, y + 0.809 * starRadius);
  ctx.lineTo(x + 0.951 * starRadius, y - 0.309 * starRadius);
  ctx.lineTo(x - 0.951 * starRadius, y - 0.309 * starRadius);
  ctx.lineTo(x + 0.588 * starRadius, y + 0.809 * starRadius);
  ctx.fill();
}
canvas {
  margin: 0;
  border: medium solid gray;
  background-color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<input id='stars' onchange='resetStars()' type='number' value='13' min='13' max='50' maxlength='2' step='1'>stars
<br>
<input id='width' onchange='resizeCanvas()' type='number' value='494' min='1' max='500' maxlength='3' step='any'>width
<br>
<input id='height' onchange='resizeCanvas()' type='number' value='350' min='1' max='500' maxlength='3' step='any'>height
<br>
<button type='button' onclick='resetStars()'>Reset Stars</button>
<button type='button' onclick='arrange()'>Arrange Stars</button>
<button type='button' onclick='stop()'>Stop</button>
<textarea id='stepsRemaining' rows='1' readonly></textarea>
<br>
<canvas id='canton' width='494' height='350'></canvas>
<br>
<textarea id='coordinateList' rows='50' cols='40' readonly></textarea>

Đầu ra cho 50 sao

(chiều rộng = 1,4, chiều cao = 1,0)

Khoảng cách trung bình ước tính là 0,06402754713808706.

Tọa độ:

0.08147037630270487, 0.07571240182553095
0.24516777356538358, 0.0803538189052793
0.431021735247462, 0.07821284835132788
0.6001163609128221, 0.08278495286739646
0.7668077034213632, 0.0821321119375313
0.941333266969696, 0.08040530195264808
1.1229190363750599, 0.07255685332834291
1.3074771164489858, 0.07681674948141588
0.09227450444336446, 0.2257047798057907
0.33559513774978766, 0.20668611954667682
0.5182463448452704, 0.23841239342827816
0.6630614113293541, 0.26097114328053417
0.821886619004045, 0.23577904321258844
1.012597304977012, 0.23308200382761057
1.174938874706673, 0.22593017096601203
1.3285181935709358, 0.24024108928169902
0.0746772556909648, 0.3920030109869904
0.23006559905554042, 0.3204287339854068
0.4086004105498357, 0.3507788129168045
0.5669847710831315, 0.4371913211100495
0.7399474422203116, 0.41599441829489137
0.9099913571857917, 0.36933063808924294
1.1170137101288482, 0.3905679602615213
1.3037811235560612, 0.3979526346564911
0.09290206345982034, 0.5678420747594305
0.23463227399157258, 0.47552307265325633
0.4042403660145938, 0.5030345851947539
0.6611151741402685, 0.5918138006294138
0.8237963249937061, 0.5663224022272697
0.9812774216782155, 0.5032518469083094
1.146386501309064, 0.570255729516661
1.3185563715676663, 0.5571870810112576
0.07541940949872694, 0.7356649763259809
0.2877585652075202, 0.6321535875762999
0.4952646673275116, 0.6343336480073624
0.6965646728710738, 0.9178076185211137
0.7903485281657828, 0.7508031981325222
0.9774998621426763, 0.6683301268754337
1.1539480102558823, 0.7513836972857155
1.3177199931376755, 0.7245296168327016
0.22215183098388988, 0.7769843436963862
0.4048364942297627, 0.7779653803681718
0.5021290208205218, 0.9254525763987298
0.6058821167972933, 0.7683130432395833
0.8777330967719849, 0.9201076171801651
0.9894820530574747, 0.8172934111543102
1.1143371956097312, 0.9265012354173626
1.3045771339439551, 0.9069856484512913
0.0930066325438706, 0.9157592790749175
0.2959676633891297, 0.9251379492518523
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.