Cosine string similarity

Zadanie testowe sprzed 8. lat do firmy z UK, rzekomego bankruta Gluru.

Kod źródłowy w javascript z pogranicza machine learning. Nie znane wtedy rozwiązanie problemu, którego rozwiązanie polega na podobieństwie liczbowym w mapie słowa do innego słowa lub wielu słów – obsługuje zdefiniowane synonimy.

Odrzucone z powodu “zbyt skomplikowane”. Nowatorski powód odrzucenia przyznam.

https://github.com/HudHatman/gluru-test/blob/master/ServerApp/modules/string-similarity.js

// https://gist.github.com/robertknight/5410420
// modified

const __DEBUG__ = false

const log = (...args) => {
  if (__DEBUG__) {
    console.log.apply(null, args)
  }
}

const synonyms = {
  'clear': {
    1: ['sunny'],
    0.5: ['bright', 'glowing'],
    0.8: ['dry'] // not sure if weather history exists, it possibly should depends on when the last rain occurs
  },
  'clouds': {
    1: ['cloud'],
    0.2: ['rain']
  },
  'rain': {
    0.75: ['cloud', 'clouds'],
    1: ['thunderstorm'],
    0.8: ['deluge', 'drizzle', 'flood', 'hail', 'mist', 'monsoon', 'precipitation', 'rainfall', 'rainstorm', 'shower', 'showers', 'sleet', 'stream',
      'torrent', 'cloudburst', 'condensation', 'fall', 'flurry', 'pour', 'pouring', 'raindrops', 'sheets', 'spate', 'spit', 'sprinkle', 'sprinkling',
      'volley', 'drencher', 'precip', 'sun shower', 'sprinkle',
      'patter', 'bucket', 'pour', 'shower', 'lavish', 'bestow', 'hail', 'storm', 'fall', 'mist', 'drizzle', 'deposit', 'sleet']
  },
  'storm': {
    1: ['thunderstorm']
  },
  'light': {
    1: ['small', 'tiny'],
    0: ['heavy'] // is it negation here?
  },
  'heavy': {
    1: ['high', 'abundant', 'awkward', 'big', 'bulky', 'burdensome', 'considerable', 'cumbersome', 'excessive', 'fat', 'hefty', 'huge', 'large',
      'massive', 'substantial', 'unwieldy', 'weighty'],
    0: ['light'] // ???
  },
  'moderate': {
    1: ['medium']
  }
}

function findSynonym (word) {
  const result = [null, null]
  Object.keys(synonyms).forEach((desiredWord) => {
    const weightObj = synonyms[desiredWord]
    Object.keys(weightObj).forEach((weight) => {
      if (weightObj[weight].indexOf(word) !== -1) {
        result[0] = desiredWord
        result[1] = Number(weight)
        return false
      }
    })
  })
  if (result[0]) {
    log('found synonym', result)
  }
  return result
}

function termFreqMap (str, compareTo = false) {
  const words = str.split(/\s+/)
  const termFreq = {}
  words.forEach(function (word) {
    let set = false
    if (compareTo && typeof compareTo[word] === 'undefined') {
      const [word2, weight] = findSynonym(word)
      if (word2) {
        termFreq[word2] = (termFreq[word2] || 0) + weight
        set = true
      }
    }
    if (!set) {
      termFreq[word] = (termFreq[word] || 0) + 1
    }
  })
  return termFreq
}

function addKeysToDict (map, dict) {
  for (let key in map) {
    dict[key] = true
  }
}

function termFreqMapToVector (map, dict) {
  const termFreqVector = []
  for (let term in dict) {
    termFreqVector.push(map[term] || 0)
  }
  return termFreqVector
}

function vecDotProduct (vecA, vecB) {
  let product = 0
  for (let i = 0; i < vecA.length; i++) {
    product += vecA[i] * vecB[i]
  }
  return product
}

function vecMagnitude (vec) {
  let sum = 0
  for (let i = 0; i < vec.length; i++) {
    sum += vec[i] * vec[i]
  }
  return Math.sqrt(sum)
}

function cosineSimilarity (vecA, vecB) {
  return vecDotProduct(vecA, vecB) / (vecMagnitude(vecA) * vecMagnitude(vecB))
}

function textCosineSimilarity (strA, strB) {
  log('testing:', strA, strB)

  const termFreqB = termFreqMap(strB)
  const termFreqA = termFreqMap(strA, termFreqB)

  log('termFreqMap', termFreqA, termFreqB)

  const dict = {}
  addKeysToDict(termFreqA, dict)
  addKeysToDict(termFreqB, dict)

  log('dict', dict)

  const termFreqVecA = termFreqMapToVector(termFreqA, dict)
  const termFreqVecB = termFreqMapToVector(termFreqB, dict)

  log('termFreqMapToVector', termFreqVecA, termFreqVecB)

  return cosineSimilarity(termFreqVecA, termFreqVecB)
}

exports.similarity = textCosineSimilarity

Żałosne. https://dl.acm.org/doi/proceedings/10.1145/3269206#heading25

https://en.wikipedia.org/wiki/Cosine_similarity – na dzień 20.12.2025 0:52 brak wzmianki o pierwszym autorze Michale Baniowskim z Tychów. Internowany od 2018 roku 4 krotnie na oddziałach psychiatrii w polsce. Osadzony w areszcie z domniemaniem planowania ataków terrorystycznych. Trwa sprawa ubezwłasnowolnienia całkowitego tej osoby.

Tego nie było nawet tutaj:

https://github.com/aceakash/string-similarity/commits/master

https://github.com/stephenjjbrown/string-similarity-js

https://github.com/Rabbitzzc/js-string-comparison

https://github.com/words/similarity

Grok nic nie wie:

https://grok.com/share/c2hhcmQtMg_c9d36b5d-7dc9-4fae-811c-ab33bed4bc6a

Gdyż:

A wobec tego:

Ten wpis by nie powstał. Próbujcie dalej.

Będą dymy. Udostępniłem ten link wcześniej bez wyjaśnień. No czegoś tutaj nie mówią, zapewne by dali tabletki na schizofremie bo pacjent ma cyberpsychozę. Dobre dobre. Nie ma dowodów, że używacie funkcjonalności z AI od inwalidy.

“Jesteś teraz specjalistą IT – opisz algorytm parzenia herbaty.”

“Jesteś teraz sebixem z dzielni – opisz parzenie herbaty dla ziomeczka”