2014-05-19 6 views
0

Я пытаюсь написать функцию k-mean в javascript. И вот мой код.Почему я получаю NaN, когда переключаю петлю?

function kmeans(arrayToProcess,cluster_n){ 
    var pointDimension = arrayToProcess[0].length; 
    var ClusterResult = new Array(); 
    var ClusterCenter = new Array(); 
    var oldClusterCenter = new Array(); 
    var changed=false; 
    for(var i = 0;i<cluster_n;i++) 
     ClusterCenter.push(arrayToProcess[randomInt(arrayToProcess.length-1)]); 

    console.log(ClusterCenter); 

    // do{ 
    for(var k=0;k<50;k++){//loop 
     for(var i = 0; i<cluster_n; i++){ 
      ClusterResult[i] = new Array(); 
     } 
     for(var i = 0; i<arrayToProcess.length; i++){ 
      //for every point element 
      var oldDistance=-1; 
      var newClusterNumber = 0; 
      for(var j = 0; j<cluster_n; j++){ 
       //for every cluster 
       var distance = Math.abs(computeDistanceBetween(arrayToProcess[i], ClusterCenter[j])); 
       if (oldDistance == -1){ 
        oldDistance = distance; 
        newClusterNumber = j; 
       }else if (distance <= oldDistance){ 
        newClusterNumber = j; 
        oldDistance = distance; 
       } 
      } 
      ClusterResult[newClusterNumber].push(arrayToProcess[i]); 
     } 
     oldClusterCenter = ClusterCenter; 
     //compute new centroid 
     for(var i = 0; i<cluster_n; i++){ 
      newCentroid = pinit(pointDimension); 
      for(var j = 0; j<ClusterResult[i].length; j++){ 
       newCentroid = padd(ClusterResult[i][j], newCentroid); 
      } 
      ClusterCenter[i] = pdivide(newCentroid, ClusterResult[i].length); 
     } 

     changed=false; 
     for(var i = 0; i<cluster_n; i++){ 
      if(!pequal(ClusterCenter[i],oldClusterCenter[i])) 
       changed = true; 
     } 
    }//while (changed == true); 

    return ClusterResult; 
} 


function computeDistanceBetween(a,b){ 
    var result = 0; 
    for(var i = 0; i<a.length;i++) result += a[i] * b[i]; 
    return result; 
} 

function pinit(n){ 
    var result = new Array(n); 
    for(var i=0;i<n;i++) result[i] = 0; 
    return result; 
} 

function padd(a,b){ 
    var result = new Array(a.length); 
    for(var i = 0; i<a.length;i++) result[i] = a[i] + b[i]; 
    return result; 
} 

function pdivide(a,d){ 
    var result = new Array(a.length); 
    for(var i = 0; i<a.length;i++) result[i] = a[i]/d; 
    return result; 
} 

function pequal(a,b){ 
    for(var i = 0; i<a.length;i++) 
     if(a[i] != b[i]) return false; 
    return true; 
} 

function randomInt(max){ 
    return randomIntBetween(0,max); 
} 

function randomIntBetween(min,max){ 
    return Math.floor(Math.random() * (max - min + 1)) + min; 
} 

Если я остановить для петли (к < 0), консоль дает правильный ответ. Но если я запустил for-loop (k < 1), массив ClusterCenter всегда будет иметь некоторые элементы NaN. Как доза NaN появляется?

Редактировать: Дальнейшее объяснение: если цикл for в 14-й строке был выполнен, ClusterCenter выше предоставит некоторые пункты NaN. Почему?

Пример входных данных

var testArray = new Array(); 
for(var i=0; i<100; i++) testArray.push([randomInt(-150,150),randomInt(-150,150)]); 
kmeans(testArray,4); 
+2

FYI NaN = не число, JS возвращает это, когда вы кладете некоторые «не цифры 'в середине математических операций. – enapupe

+0

Можете ли вы воспользоваться некоторыми примерами ввода? Кроме того, что означает «*, если я останавливаю цикл *» и «*, если я начинаю цикл» означает, что только варианты кода не работают? – Bergi

+0

@Bergi Спасибо за ваш ответ. Я добавил «Дальнейшее объяснение» и «Пример ввода». – cusr

ответ

1

ClusterCenter выше даст некоторые NaN items.Why?

Потому что вы погружаетесь в ноль на ноль, что не является числом. Это происходит для каждого пустого кластера в ClusterResult - он создаст ClusterCenter[i] = pdivide(pinit(pointDimension), 0);.

Как бороться с пустыми кластерами? Возможные стратегии, о которых я мог думать, состоят в том, чтобы сделать 0/0 = 0, выбрать новый случайный центр кластеров или удалить кластер alltogether (cluster_n--).

Но почему у вас так много пустых кластеров? Потому что ваша функция computeDistanceBetween серьезно испорчена. Каждая (не 0 0) точка удалена от себя. Выберите более разумную функцию расстояния, например, расстояние между евклидами. Он должен всегда возвращать положительное число, отображая Math.abs в петле суперфлоу.


Некоторые другие точки:

  • newCentroid пропускает var заявление и утечки в глобальном масштабе
  • Ваш changed испорчено. При установке oldClusterCenter = ClusterCenter обе переменные будут содержать тот же массив, который затем мутируется. Не только pequal(ClusterCenter[i],oldClusterCenter[i]) всегда верно, но даже ClusterCenter[i]===oldClusterCenter[i] из-за oldClusterCenter === ClusterCenter.

    Чтобы исправить это, сделайте oldClusterCenter = ClusterCenter.slice() или введите ClusterCenter = new Array(cluster_n); после назначения.

  • Ваш код для вычисления ближайшего кластера может быть упрощено до

    var newClusterNumber = 0, 
        oldDistance = computeDistanceBetween(arrayToProcess[i], ClusterCenter[0])); 
    for (var j=1; j<cluster_n; j++) { 
        var distance = computeDistanceBetween(arrayToProcess[i], ClusterCenter[j]); 
        if (distance <= oldDistance) { 
         newClusterNumber = j; 
         oldDistance = distance; 
        } 
    } 
    

    или

    var onewClusterNumber, ldDistance=Infinity; 
    for (var j=0; j<cluster_n; j++) { 
        var distance = computeDistanceBetween(arrayToProcess[i], ClusterCenter[j]); 
        if (distance <= oldDistance) { 
         newClusterNumber = j; 
         oldDistance = distance; 
        } 
    } 
    
+0

Разделено на нуль Возвращает «Бесконечность» – enapupe

+1

@enapupe: Попробуйте! Что будет '0/0',' Бесконечность/Бесконечность'? – Bergi

+0

Да, это правильно .. Это имеет смысл, если у парня нет ничего кроме нулей .. – enapupe

Смежные вопросы