1 2 //////////////////////////////////////////////////////////////////////////////// 3 /** 4 * @fileOverview Copyright (C) 2009 www.webis.de 5 * @author Christof Braeutigam christof.braeutigam@uni-weimar.de 6 * @author Alexander Kuemmel alexander.kuemmel@uni-weimar.de 7 * @version 0.1 8 */ 9 10 /** 11 * A data-structure that representes a mathematical vector 12 * with a set of essential functions to handle some vector instances. 13 * @class Representation of a vector 14 * @constructor 15 * @author Alexander Kümmel alexander.kuemmel@uni-weimar.de 16 * @author Christof Bräutigam christof.braeutigam@uni-weimar.de 17 */ 18 de.aitools.js.Vector = function () { 19 /* private part */ 20 21 /** 22 * @private 23 * @type {Number} 24 */ 25 var dimension_ = 0; 26 /** 27 * @private 28 * @type {Boolean} 29 */ 30 var isNormalized_ = false; 31 /** 32 * @private 33 * @type {Object} 34 */ 35 var hashMap_ = {}; 36 /** 37 * @private 38 * @type {Number} 39 */ 40 var size_ = 0; 41 //self reference for unprivilidged methods 42 //var self_ = this; 43 44 /* public part */ 45 46 /** 47 * Returns a duplicate of this {@link de.aitools.js.Vector} 48 * @public 49 * @returns {de.aitools.js.Vector} Cloned vector 50 */ 51 this.clone = function () { 52 var clone = new de.aitools.js.Vector(); 53 var i = null; 54 for (i in hashMap_) { 55 if (hashMap_.hasOwnProperty(i)) { 56 //dimension_ and size_ are setup automatically 57 clone.setValue(parseInt(i,10), this.getValue(i)); 58 } 59 } 60 if (this.isNormalized()) { 61 clone.normalize(); 62 } 63 return clone; 64 }; 65 66 /** 67 * Compares this instance with the given {@link de.aitools.js.Vector} and 68 * returns the result 69 * @param {de.aitools.js.Vector} vector A {@link de.aitools.js.Vector} instance 70 * @public 71 * @returns {Boolean} True if equals 72 */ 73 this.equals = function (vector) { 74 var check = true; 75 check = check && (this.getDimension() === vector.getDimension()); 76 check = check && (this.isNormalized() === vector.isNormalized()); 77 var i = null; 78 for (i in hashMap_) { 79 if (hashMap_.hasOwnProperty(i)) { 80 check = check && (this.getValue(i) === vector.getValue(i)); 81 } 82 } 83 check = check && (this.getElementCount() === vector.getElementCount()); 84 return check; 85 }; 86 87 /** 88 * Add the given {@link de.aitools.js.Vector} to this one 89 * @param {de.aitools.js.Vector} vector A {@link de.aitools.js.Vector} instance 90 * @public 91 */ 92 this.add = function (vector) { 93 var dim = (this.getDimension() > vector.getDimension) ? this.getDimension() 94 : vector.getDimension(); 95 for (var i = 0; i < dim; i++) { 96 var src1 = this.getValue(i); 97 var src2 = vector.getValue(i); 98 //to prevent storage waste, add only values != 0 99 if (src1 === 0 && src2 === 0) { continue; } 100 var sum = this.getValue(i)+vector.getValue(i); 101 if (sum !== 0) { hashMap_[i] = sum; } 102 } 103 isNormalized_ = false; 104 }; 105 106 /** 107 * Scales each component according to the specified factor 108 * @param {Number} factor Numeric value 109 * @public 110 */ 111 this.scale = function (factor) { 112 var i = null; 113 for (i in hashMap_) { 114 if (hashMap_.hasOwnProperty(i)) { 115 hashMap_[i] = hashMap_[i] * factor; 116 } 117 } 118 isNormalized_ = false; 119 }; 120 121 /** 122 * Multiplies this and the given {@link de.aitools.js.Vector} and 123 * stores the results within this one 124 * @param {de.aitools.js.Vector} vector A {@link de.aitools.js.Vector} instance 125 * @public 126 */ 127 this.multiply = function (vector) { 128 //CHECK_TRUE(this.getDimension() === vector.getDimension(), "different dimensions not allowed"); 129 var indices = []; 130 if (this.getElementCount() < vector.getElementCount()) { 131 indices = this.getIndices(); 132 } 133 else { indices = vector.getIndices(); } 134 for (var i = 0; i < indices.length; i++) { 135 var product = this.getValue(indices[i]) * vector.getValue(indices[i]); 136 hashMap_[indices[i]] = product; 137 } 138 isNormalized_ = false; 139 }; 140 141 /** 142 * Computes and returns the scalar product between this and the given {@link de.aitools.js.Vector} 143 * @param {de.aitools.js.Vector} vector A {@link de.aitools.js.Vector} instance 144 * @public 145 * @returns {Number} 146 */ 147 this.scalarProduct = function (vector) { 148 //CHECK_TRUE(this.getDimension() === vector.getDimension(), "different dimensions not allowed"); 149 var indices = []; 150 if (this.getElementCount() < vector.getElementCount()) { 151 indices = this.getIndices(); 152 } 153 else { indices = vector.getIndices(); } 154 155 var sum = 0; 156 for (var i = 0; i < indices.length; i++) { 157 sum += this.getValue(indices[i]) * vector.getValue(indices[i]); 158 } 159 return sum; 160 }; 161 162 /** 163 * Get the value at the specified index 164 * @param {Number} index 165 * @public 166 * @returns {Number} 167 */ 168 this.getValue = function (index) { 169 var val = hashMap_[index]; 170 if (val === undefined) { 171 return 0; 172 } 173 return val; 174 }; 175 176 /** 177 * Get an array of all values within this {@link de.aitools.js.Vector} 178 * @public 179 * @returns {Array} Array of Numbers 180 */ 181 this.getValues = function () { 182 var arr = []; 183 var i = null; 184 for (i in hashMap_) { 185 if (hashMap_.hasOwnProperty(i)) { arr.push(hashMap_[i]); } 186 } 187 return arr; 188 }; 189 190 /** 191 * Set a value at a specific index in this {@link de.aitools.js.Vector} 192 * @param {Number} index 193 * @param {Number} value 194 * @public 195 */ 196 this.setValue = function (index, value) { 197 //CHECK_TYPE(index, "number", "index has to be numeric"); 198 //var oldLen = hashMap_.length(); 199 var isNewIndex = (hashMap_[index] === undefined) ? true : false; 200 if (value !== 0) { hashMap_[index] = value; } 201 else { delete hashMap_[index]; } // TODO: wieso das denn? 202 //size_ = (oldLen < hashMap_.length())? size_ + 1 : size_; 203 if (isNewIndex) { size_ += 1; } 204 dimension_ = (index + 1 > dimension_) ? index + 1 : dimension_; 205 isNormalized_ = false; 206 }; 207 208 /** 209 * Set the dimension of this {@link de.aitools.js.Vector} 210 * @param {Number} dimension 211 * @public 212 */ 213 this.setDimension = function (dimension) { 214 CHECK_TYPE(dimension,"number","dimension has to be numeric"); 215 dimension_ = dimension; 216 }; 217 218 /** 219 * Normalizes this {@link de.aitools.js.Vector} 220 * @public 221 */ 222 this.normalize = function () { 223 if (!isNormalized_) { 224 var squareSum = 0; 225 var values = this.getValues(); 226 for (var i = 0; i < values.length; i++) { 227 squareSum += values[i] * values[i]; 228 } 229 if (squareSum !== 0) { 230 var factor = 1 / Math.sqrt(squareSum); 231 this.scale(factor); 232 } 233 isNormalized_ = true; 234 } 235 }; 236 237 /** 238 * Returns the normalized state of this {@link de.aitools.js.Vector} 239 * @public 240 * @returns {Boolean} True if already normalized 241 */ 242 this.isNormalized = function () { return isNormalized_; }; 243 244 /** 245 * Get the deminsion of this {@link de.aitools.js.Vector} 246 * @public 247 * @returns {Number} 248 */ 249 this.getDimension = function () { return dimension_; }; 250 251 /** 252 * Get the number of Elementens within this {@link de.aitools.js.Vector} 253 * @public 254 * @returns {Number} 255 */ 256 this.getElementCount = function () { return size_; }; 257 258 /** 259 * Get a array of all the indices of this {@link de.aitools.js.Vector} 260 * @public 261 * @returns {Array} Array of numbers 262 */ 263 this.getIndices = function () { 264 var arr = []; 265 var i = null; 266 for (i in hashMap_) { 267 if (hashMap_.hasOwnProperty(i)) { arr.push(i); } 268 } 269 return arr; 270 }; 271 }; 272 273 /** 274 * Computes the highest dimension of over all given 275 * {@link de.aitools.js.Vector} instances 276 * @param {Array} vectorArr Array of {@link de.aitools.js.Vector} instances 277 * @static 278 * @function 279 * @returns {Number} 280 */ 281 de.aitools.js.Vector.getMaxDimension = function (vectorArr) { 282 var maxDimension = 0; 283 for (var i = 0; i < vectorArr.length; i++) { 284 var curDimension = vectorArr[i].getDimension(); 285 if (curDimension > maxDimension) { maxDimension = curDimension; } 286 } 287 return maxDimension; 288 }; 289 290 /** 291 * Computes the centroid over all given {@link de.aitools.js.Vector} instances 292 * @param {Array} vectorArr Array of {@link de.aitools.js.Vector} instances 293 * @static 294 * @function 295 * @returns {de.aitools.js.Vector} 296 */ 297 de.aitools.js.Vector.computeCentroid = function (vectorArr) { 298 var centroid = new de.aitools.js.Vector(); 299 for (var i = 0; i < vectorArr.length; i++) { 300 centroid.add(vectorArr[i]); 301 } 302 centroid.scale(1.0 / vectorArr.length); 303 return centroid; 304 }; 305 306 // Vector.js 307 //////////////////////////////////////////////////////////////////////////////// 308