js/nn1.php
<html>
<head>
<title> <?php echo basename(__file__, '.php'); ?> </title>
<style type="text/css">
#src {
display: block; width: 250px; height: 250px; text-align: center;
}
/* #c000, #c001, #c010, #c011, #c100, #c101, #c110, #c111 {
display: block; float:left; width: 60px; height: 60px; line-height: 60px; font-size: 12px;
}
#c000 { background-color: #000; color: #fff; }
#c001 { background-color: #00f; color: #fff; }
#c010 { background-color: #0f0; color: #fff; }
#c011 { background-color: #0ff; color: #000; }
#c100 { background-color: #f00; color: #fff; }
#c101 { background-color: #f0f; color: #000; }
#c110 { background-color: #ff0; color: #000; }
#c111 { background-color: #fff; color: #000; } */
#r { clear: both;
display: block; width: 500px; height: 500px; background-color: green; text-align: center; font-size: 12px;
}
</style>
<script type="text/javascript">
var rc = -1;
var aa = []; // result array [row, col, r g b]
var sc = []; // source colore [x, r g b]
var it = 0;
function numCols(nCo) { /* draw source colors */
var co = parseInt(nCo);
var h = '';
sc = [];
if (isNaN(co) || co < 2) {
h = 'change num colors to illegal ' + nCo;
} else {
var w = Math.floor(250 / Math.ceil(Math.sqrt(co))); /* width of one source cell */
var d = Math.ceil(Math.cbrt(co)); /* how many steps for each of the 3 base colors */
var d3 = d * d * d - 1;
var st = 'style="display:block; float: left;width:' + w + 'px; height: ' + w + 'px; line-height: ' + w + 'px; ';
var cf = 15 / (d-1);
for (var x=0; x < co; x++) {
var cc = Math.round(d3 / (co-1) * x); /* current color index */
var ca = [Math.round(Math.floor(cc / d / d) * cf), Math.round(Math.floor(cc / d) % d * cf), Math.round(cc % d % d * cf)]; /* rgb fff value */
var ct = colStr(ca);
h += '<div ' + st + ' background-color: ' + ct + '; color: ' + (ca[0] + ca[1] + ca[2] < 24 ? '#fff' : '#000') + ';">'
+ ct + '</div>';
sc.push(ca);
}
}
document.getElementById("src").innerHTML = h;
numRCcha(document.getElementById("numRC").value);
}
function colStr(c) { /* return the color string #fff from the rgb color c[0] c[1] c[2], each in [0, 15] */
return '#' + Math.round(c[0]).toString(16) + Math.round(c[1]).toString(16) + Math.round(c[2]).toString(16);
}
function numRCcha(nRc) { /* randomly initialize the nRc*nRc map colors */
rc = parseInt(nRc);
if (isNaN(rc) || rc < 1) {
alert('change num row/cols to illegal ' + nRc);
} else {
aa = [];
for (var i = 0; i < rc; i++ ) {
rr = [];
for (var j = 0; j < rc; j++ ) {
// rr.push([7,0,12]);
rr.push([Math.floor(Math.random() * 16), Math.floor(Math.random() * 16), Math.floor(Math.random() * 16)]);
}
aa.push(rr);
}
// alert('change num row/cols to legal ' + rc);
}
it = 0;
draw();
document.getElementById("rTxt").innerHTML = 'randomly initialised s-m-dist**2=' + Math.round(smDist) + ' m-m-dist**2=' + Math.round(mmDist);
}
var chg = 0;
function iterate(num) { /* one iteration: move each map color near to the winner (by epW) and neiboughrs nearer by epN */
if (rc < 1) {
document.getElementById("rTxt").innerHTML = 'cannot iterate illegal rows/cols=' + rc;
return;
}
else if (sc.length < 2) {
document.getElementById("rTxt").innerHTML = 'cannot iterate empty src';
return;
}
for ( var c=0; c < num; c++) {
iterate1();
}
draw();
document.getElementById("rTxt").innerHTML = 'iteration ' + it + ' s-m-dist**2=' + Math.round(smDist) + ' m-m-dist**2=' + Math.round(mmDist) + ' chg=' + chg;
}
function iterate1() {
var epW = 0.003;
var epN = epW/5;
chg = 0;
for (var x=0; x < sc.length; x++) {
var y = x;
y = Math.floor(Math.random() * sc.length)
var bi = 0, bj = 0;
for (var i = 0; i < rc; i++ )
for (var j = 0; j < rc; j++ ) {
if ( dist2(sc[y], aa[i][j]) < dist2(sc[y], aa[bi][bj])) {
bi = i;
bj = j;
}
}
upd(aa[bi][bj], sc[y], epW); // update winner
if (bi >= 1) upd(aa[bi-1][bj], sc[y], epN); // update neighbours
if (bi < rc-1) upd(aa[bi+1][bj], sc[y], epN);
if (bj >= 1) upd(aa[bi][bj-1], sc[y], epN);
if (bj < rc-1) upd(aa[bi][bj+1], sc[y], epN);
}
it++;
}
function minToMax() {
info();
var ai = 0, aj=0, bi= 0, bj = 0;
for (var i = 0; i < rc; i++ ) {
for (var j = 0; j < rc; j++ ) {
if (aa[i][j][3].length < aa[ai][aj][3].length) {
ai = i;
aj = j;
}
if (aa[i][j][4] > aa[bi][bj][4] || ( aa[i][j][4] == aa[bi][bj][4] && aa[i][j][3].length > aa[bi][bj][3].length)) {
bi = i;
bj = j;
}
}
}
if (ai == bi && aj == bj) {
alert("max = min: " + ai + ',' + aj);
return;
} else {
var m = 'moved ' + ai + ',' + aj + '(' + aa[ai][aj][3].length + ' ' + Math.round(aa[ai][aj][4]) + ') to '
+ bi + ',' + bj + '(' + aa[bi][bj][3].length + ' ' + Math.round(aa[bi][bj][4]) + ')';
x = aa[bi][bj][3][Math.floor(Math.random() * aa[bi][bj][3].length)];
aa[ai][aj] = [sc[x][0], sc[x][1], sc[x][2]];
draw();
document.getElementById("rTxt").innerHTML = m + ' s-m-dist**2=' + Math.round(smDist) + ' m-m-dist**2=' + Math.round(mmDist) + ' chg=' + chg;
}
}
function dist2(a, b) { /* return square of euclidean distance */
return (a[0] - b[0]) * (a[0] - b[0])
+ (a[1] - b[1]) * (a[1] - b[1])
+ (a[2] - b[2]) * (a[2] - b[2]);
}
function upd(u, o, eps) { /* update map color ==> move u on line u - o, to point 1-eps : eps obetween */
var fi = 1 - eps;
var n = u[0] * fi + o[0] * eps;
chg += (u[0] - n) * (u[0] - n);
u[0] = n;
n = u[1] * fi + o[1] * eps;
chg += (u[1] - n) * (u[1] - n);
u[1] = n;
n = u[2] * fi + o[2] * eps;
chg += (u[2] - n) * (u[2] - n);
u[2] = n;
return;
}
var mmDist = 0; /* for source color find winner and distance */
var smDist = 0;
function draw() { /* draw map */
if (isNaN(rc) || rc < 1) {
document.getElementById("r").innerHTML = 'change num row/cols to illegal ' + event.target.value;
return -99;
}
info();
var h = '';
var w = Math.trunc(500 / rc);
var st = 'style="display:block; float: left;width:' + w + 'px; height: ' + w + 'px;'
for (var i = 0; i < rc; i++ ) {
for (var j = 0; j < rc; j++ ) {
var id = 'r' + i + ' c' + j;
var co = colStr(aa[i][j]);
h += '<div id="' + id + '" ' + st + ' background-color: ' + co
+ '; color: ' + (aa[i][j][0]+aa[i][j][1]+aa[i][j][2] < 24 ? '#fff' : '#000') + ';">'
+ id + ' ' + co;
if (aa[i][j][3].length != 0)
h += '<br/>winner=' + aa[i][j][3].length + ' dist2=' + Math.round(aa[i][j][4]) + '<br/>' + aa[i][j][5];
h += '</div> ';
}
}
document.getElementById("r").innerHTML = h;
}
function info() {
mmDist = 0;
smDist = 0;
for (var i = 0; i < rc; i++ )
for (var j = 0; j < rc; j++ ) {
aa[i][j][3] = [];
aa[i][j][4] = 0;
aa[i][j][5] = '';
if (i >= 1)
mmDist += dist2(aa[i-1][j], aa[i][j]);
if (j >= 1)
mmDist += dist2(aa[i][j-1], aa[i][j]);
}
for (var x=0; x < sc.length; x++) {
var bi = 0, bj = 0;
for (var i = 0; i < rc; i++ ) {
for (var j = 0; j < rc; j++ ) {
if ( dist2(sc[x], aa[i][j]) < dist2(sc[x], aa[bi][bj])) {
bi = i;
bj = j;
}
}
}
aa[bi][bj][3].push(x);
var d = dist2(aa[bi][bj], sc[x]);
smDist += d;
aa[bi][bj][4] += d;
aa[bi][bj][5] += colStr(sc[x]) + ' ';
}
}
function onLoadDo() {
numCols(document.getElementById("numCols").value);
}
</script>
</head>
<body onload="onLoadDo();">
<ol>
<h1>self organizing map</h1>
<ul><li>select number of source colors
</li><li>fill y*y map with random colors
</li><li>iterate: for each source color
<ul><li> find nearest map color = winner
</li><li>move winner nearer to source (by epW)
</li><li>move neighbours of winner nearer to source (by epN typically < epW)
</li></ul>
<li><strong>unfortuneatly</strong>, iteration does not improve over random. Better are small eps (0.01 or less)
</li><li>possiply each map neuron should have the same number of winners! Verbesserungen?!Idee: minToMax: move map points with the fewest winners to a randomly chosen source color of the neurons with the top distanceSquare
</li></ul>
<h2>source colors rgb</h2>
number of source colors <input type="text" id="numCols" value="20" size="2" onchange="numCols(event.target.value);">
<div id="src">
<div id="c000">000</div>
<div id="c001">001</div>
<div id="c010">010</div>
<div id="c011">011</div>
<div id="c100">100</div>
<div id="c101">101</div>
<div id="c110">110</div>
<div id="c111">111</div>
</div>
<h2 style="clear: both;">result square map</h2>
<ul>
<li>number of rows/cols <input type="text" id="numRC" value="2" size="2" onchange="numRCcha(event.target.value);"></li>
<li> <input type="button" value="iterate1" onclick="iterate(1)" /> <input type="button" value="iterate100" onclick="iterate(100)" /> <input type="button" value="minToMax" onclick="minToMax()" /> </li>
<li id="rTxt">initial</li></ul><br/>
<div id="r">
</div>
<h1 style="clear: both;">Source <?php echo __file__; ?> </h1>
<?php highlight_file(__file__) ?>
</body>
</html>