Javascript - ortalama mesafe azalırsa yıldızı rasgele hareket ettir
(işlemin bir animasyonuyla)
Bu benim ilk cevabım gibi yoğun bir animasyon sunmuyor, potansiyel düzenlemeler test edilip reddedildiği için hareketsiz uzun süreli. Bununla birlikte, nihai sonuç daha düşük bir ortalama mesafeye sahiptir, bu nedenle bu yöntem bir gelişmedir.
Yaklaşım hala çok basittir:
- Rasgele bir yıldız seçin
- Rastgele bir yönde rastgele bir mesafe taşıyın
- Ortalama mesafe azalırsa, yeni konumu koruyun
Bu işlem, yıldızların hareket ettiği miktarı yavaş yavaş azaltarak, çok sayıda tekrarlanır. Hareketli mesafenin rastgele seçimi, daha küçük mesafelere doğru eğilimlidir, bu nedenle ilerleme, ara sıra daha büyük atlama ile serpiştirilmiş küçük değişikliklerle gerçekleşir. Her adım benim ilk cevabımdan daha uzun sürüyor, çünkü ortalama mesafeyi ölçmek kantonun tamamını örneklemeyi gerektiren yavaş bir işlemdir.
Sorunun gerektirdiği gibi, bu, yerleşik rasgele işlevini kullanmaz, bunun yerine xorshift işlevini kullanır .
Kodun çoğu kurulum ve animasyonu kapsar - algoritmayı uygulayan kısım fonksiyondur adjustStars
.
kod
Sürmekte olan işlemi aşağıdaki Yığın Parçacığında izleyebilirsiniz.
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>
50 yıldız için çıktı
(genişlik = 1,4, yükseklik = 1,0)
Ortalama mesafe 0.06402754713808706 olarak tahmin edildi.
Koordinatlar:
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