|
|
|
|
using System;
|
|
|
|
|
|
|
|
|
|
namespace ZeroLevel.Services.Mathemathics
|
|
|
|
|
{
|
|
|
|
|
public enum KnownMetrics
|
|
|
|
|
{
|
|
|
|
|
Cosine, Manhattanm, Euclide, Chebyshev, DotProduct
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static class Metrics
|
|
|
|
|
{
|
|
|
|
|
public static Func<float[], float[], double> CreateFloat(KnownMetrics metric)
|
|
|
|
|
{
|
|
|
|
|
switch (metric)
|
|
|
|
|
{
|
|
|
|
|
case KnownMetrics.Euclide:
|
|
|
|
|
return new Func<float[], float[], double>((u, v) => L2EuclideanDistance(u, v));
|
|
|
|
|
case KnownMetrics.Cosine:
|
|
|
|
|
return new Func<float[], float[], double>((u, v) => CosineDistance(u, v));
|
|
|
|
|
case KnownMetrics.Chebyshev:
|
|
|
|
|
return new Func<float[], float[], double>((u, v) => ChebyshevDistance(u, v));
|
|
|
|
|
case KnownMetrics.Manhattanm:
|
|
|
|
|
return new Func<float[], float[], double>((u, v) => L1ManhattanDistance(u, v));
|
|
|
|
|
case KnownMetrics.DotProduct:
|
|
|
|
|
return new Func<float[], float[], double>((u, v) => DotProductDistance(u, v));
|
|
|
|
|
}
|
|
|
|
|
throw new Exception($"Metric '{metric.ToString()}' not supported for Float type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Func<byte[], byte[], double> CreateByte(KnownMetrics metric)
|
|
|
|
|
{
|
|
|
|
|
switch (metric)
|
|
|
|
|
{
|
|
|
|
|
case KnownMetrics.Euclide:
|
|
|
|
|
return new Func<byte[], byte[], double>((u, v) => L2EuclideanDistance(u, v));
|
|
|
|
|
case KnownMetrics.Cosine:
|
|
|
|
|
return new Func<byte[], byte[], double>((u, v) => CosineDistance(u, v));
|
|
|
|
|
case KnownMetrics.Chebyshev:
|
|
|
|
|
return new Func<byte[], byte[], double>((u, v) => ChebyshevDistance(u, v));
|
|
|
|
|
case KnownMetrics.Manhattanm:
|
|
|
|
|
return new Func<byte[], byte[], double>((u, v) => L1ManhattanDistance(u, v));
|
|
|
|
|
case KnownMetrics.DotProduct:
|
|
|
|
|
return new Func<byte[], byte[], double>((u, v) => DotProductDistance(u, v));
|
|
|
|
|
}
|
|
|
|
|
throw new Exception($"Metric '{metric.ToString()}' not supported for Byte type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Func<long[], long[], double> CreateLong(KnownMetrics metric)
|
|
|
|
|
{
|
|
|
|
|
switch (metric)
|
|
|
|
|
{
|
|
|
|
|
case KnownMetrics.Euclide:
|
|
|
|
|
return new Func<long[], long[], double>((u, v) => L2EuclideanDistance(u, v));
|
|
|
|
|
case KnownMetrics.Cosine:
|
|
|
|
|
return new Func<long[], long[], double>((u, v) => CosineDistance(u, v));
|
|
|
|
|
case KnownMetrics.Chebyshev:
|
|
|
|
|
return new Func<long[], long[], double>((u, v) => ChebyshevDistance(u, v));
|
|
|
|
|
case KnownMetrics.Manhattanm:
|
|
|
|
|
return new Func<long[], long[], double>((u, v) => L1ManhattanDistance(u, v));
|
|
|
|
|
case KnownMetrics.DotProduct:
|
|
|
|
|
return new Func<long[], long[], double>((u, v) => DotProductDistance(u, v));
|
|
|
|
|
}
|
|
|
|
|
throw new Exception($"Metric '{metric.ToString()}' not supported for Long type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Func<int[], int[], double> CreateInt(KnownMetrics metric)
|
|
|
|
|
{
|
|
|
|
|
switch (metric)
|
|
|
|
|
{
|
|
|
|
|
case KnownMetrics.Euclide:
|
|
|
|
|
return new Func<int[], int[], double>((u, v) => L2EuclideanDistance(u, v));
|
|
|
|
|
case KnownMetrics.Cosine:
|
|
|
|
|
return new Func<int[], int[], double>((u, v) => CosineDistance(u, v));
|
|
|
|
|
case KnownMetrics.Chebyshev:
|
|
|
|
|
return new Func<int[], int[], double>((u, v) => ChebyshevDistance(u, v));
|
|
|
|
|
case KnownMetrics.Manhattanm:
|
|
|
|
|
return new Func<int[], int[], double>((u, v) => L1ManhattanDistance(u, v));
|
|
|
|
|
case KnownMetrics.DotProduct:
|
|
|
|
|
return new Func<int[], int[], double>((u, v) => DotProductDistance(u, v));
|
|
|
|
|
}
|
|
|
|
|
throw new Exception($"Metric '{metric.ToString()}' not supported for Int type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The taxicab metric is also known as rectilinear distance,
|
|
|
|
|
/// L1 distance or L1 norm, city block distance, Manhattan distance,
|
|
|
|
|
/// or Manhattan length, with the corresponding variations in the name of the geometry.
|
|
|
|
|
/// It represents the distance between points in a city road grid.
|
|
|
|
|
/// It examines the absolute differences between the coordinates of a pair of objects.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static float L1ManhattanDistance(float[] v1, float[] v2)
|
|
|
|
|
{
|
|
|
|
|
float res = 0;
|
|
|
|
|
for (int i = 0; i < v1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
float t = v1[i] - v2[i];
|
|
|
|
|
res += t * t;
|
|
|
|
|
}
|
|
|
|
|
return (res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float L1ManhattanDistance(byte[] v1, byte[] v2)
|
|
|
|
|
{
|
|
|
|
|
float res = 0;
|
|
|
|
|
for (int i = 0; i < v1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
float t = v1[i] - v2[i];
|
|
|
|
|
res += t * t;
|
|
|
|
|
}
|
|
|
|
|
return (res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float L1ManhattanDistance(int[] v1, int[] v2)
|
|
|
|
|
{
|
|
|
|
|
float res = 0;
|
|
|
|
|
for (int i = 0; i < v1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
float t = v1[i] - v2[i];
|
|
|
|
|
res += t * t;
|
|
|
|
|
}
|
|
|
|
|
return (res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float L1ManhattanDistance(long[] v1, long[] v2)
|
|
|
|
|
{
|
|
|
|
|
float res = 0;
|
|
|
|
|
for (int i = 0; i < v1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
float t = v1[i] - v2[i];
|
|
|
|
|
res += t * t;
|
|
|
|
|
}
|
|
|
|
|
return (res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Euclidean distance is the most common use of distance.
|
|
|
|
|
/// Euclidean distance, or simply 'distance',
|
|
|
|
|
/// examines the root of square differences between the coordinates of a pair of objects.
|
|
|
|
|
/// This is most generally known as the Pythagorean theorem.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static float L2EuclideanDistance(float[] v1, float[] v2)
|
|
|
|
|
{
|
|
|
|
|
float res = 0;
|
|
|
|
|
for (int i = 0; i < v1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
float t = v1[i] - v2[i];
|
|
|
|
|
res += t * t;
|
|
|
|
|
}
|
|
|
|
|
return (float)Math.Sqrt(res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float L2EuclideanDistance(byte[] v1, byte[] v2)
|
|
|
|
|
{
|
|
|
|
|
float res = 0;
|
|
|
|
|
for (int i = 0; i < v1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
float t = v1[i] - v2[i];
|
|
|
|
|
res += t * t;
|
|
|
|
|
}
|
|
|
|
|
return (float)Math.Sqrt(res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float L2EuclideanDistance(int[] v1, int[] v2)
|
|
|
|
|
{
|
|
|
|
|
float res = 0;
|
|
|
|
|
for (int i = 0; i < v1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
float t = v1[i] - v2[i];
|
|
|
|
|
res += t * t;
|
|
|
|
|
}
|
|
|
|
|
return (float)Math.Sqrt(res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float L2EuclideanDistance(long[] v1, long[] v2)
|
|
|
|
|
{
|
|
|
|
|
float res = 0;
|
|
|
|
|
for (int i = 0; i < v1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
float t = v1[i] - v2[i];
|
|
|
|
|
res += t * t;
|
|
|
|
|
}
|
|
|
|
|
return (float)Math.Sqrt(res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The general metric for distance is the Minkowski distance.
|
|
|
|
|
/// When lambda is equal to 1, it becomes the city block distance (L1),
|
|
|
|
|
/// and when lambda is equal to 2, it becomes the Euclidean distance (L2).
|
|
|
|
|
/// The special case is when lambda is equal to infinity (taking a limit),
|
|
|
|
|
/// where it is considered as the Chebyshev distance.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static float MinkowskiDistance(float[] v1, float[] v2, int order)
|
|
|
|
|
{
|
|
|
|
|
int count = v1.Length;
|
|
|
|
|
double sum = 0.0;
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
sum = sum + Math.Pow(Math.Abs(v1[i] - v2[i]), order);
|
|
|
|
|
}
|
|
|
|
|
return (float)Math.Pow(sum, (1 / order));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float MinkowskiDistance(byte[] v1, byte[] v2, int order)
|
|
|
|
|
{
|
|
|
|
|
int count = v1.Length;
|
|
|
|
|
double sum = 0.0;
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
sum = sum + Math.Pow(Math.Abs(v1[i] - v2[i]), order);
|
|
|
|
|
}
|
|
|
|
|
return (float)Math.Pow(sum, (1 / order));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float MinkowskiDistance(int[] v1, int[] v2, int order)
|
|
|
|
|
{
|
|
|
|
|
int count = v1.Length;
|
|
|
|
|
double sum = 0.0;
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
sum = sum + Math.Pow(Math.Abs(v1[i] - v2[i]), order);
|
|
|
|
|
}
|
|
|
|
|
return (float)Math.Pow(sum, (1 / order));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float MinkowskiDistance(long[] v1, long[] v2, int order)
|
|
|
|
|
{
|
|
|
|
|
int count = v1.Length;
|
|
|
|
|
double sum = 0.0;
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
sum = sum + Math.Pow(Math.Abs(v1[i] - v2[i]), order);
|
|
|
|
|
}
|
|
|
|
|
return (float)Math.Pow(sum, (1 / order));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Chebyshev distance is also called the Maximum value distance,
|
|
|
|
|
/// defined on a vector space where the distance between two vectors is
|
|
|
|
|
/// the greatest of their differences along any coordinate dimension.
|
|
|
|
|
/// In other words, it examines the absolute magnitude of the differences
|
|
|
|
|
/// between the coordinates of a pair of objects.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static double ChebyshevDistance(float[] v1, float[] v2)
|
|
|
|
|
{
|
|
|
|
|
int count = v1.Length;
|
|
|
|
|
float max = float.MinValue;
|
|
|
|
|
float c;
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
c = Math.Abs(v1[i] - v2[i]);
|
|
|
|
|
if (c > max)
|
|
|
|
|
{
|
|
|
|
|
max = c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return max;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static double ChebyshevDistance(byte[] v1, byte[] v2)
|
|
|
|
|
{
|
|
|
|
|
int count = v1.Length;
|
|
|
|
|
float max = float.MinValue;
|
|
|
|
|
float c;
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
c = Math.Abs(v1[i] - v2[i]);
|
|
|
|
|
if (c > max)
|
|
|
|
|
{
|
|
|
|
|
max = c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return max;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static double ChebyshevDistance(int[] v1, int[] v2)
|
|
|
|
|
{
|
|
|
|
|
int count = v1.Length;
|
|
|
|
|
float max = float.MinValue;
|
|
|
|
|
float c;
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
c = Math.Abs(v1[i] - v2[i]);
|
|
|
|
|
if (c > max)
|
|
|
|
|
{
|
|
|
|
|
max = c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return max;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static double ChebyshevDistance(long[] v1, long[] v2)
|
|
|
|
|
{
|
|
|
|
|
int count = v1.Length;
|
|
|
|
|
float max = float.MinValue;
|
|
|
|
|
float c;
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
c = Math.Abs(v1[i] - v2[i]);
|
|
|
|
|
if (c > max)
|
|
|
|
|
{
|
|
|
|
|
max = c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return max;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float CosineDistance(float[] u, float[] v)
|
|
|
|
|
{
|
|
|
|
|
if (u.Length != v.Length)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Vectors have non-matching dimensions");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float dot = 0.0f;
|
|
|
|
|
float nru = 0.0f;
|
|
|
|
|
float nrv = 0.0f;
|
|
|
|
|
for (int i = 0; i < u.Length; ++i)
|
|
|
|
|
{
|
|
|
|
|
dot += u[i] * v[i];
|
|
|
|
|
nru += u[i] * u[i];
|
|
|
|
|
nrv += v[i] * v[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var similarity = dot / (float)(Math.Sqrt(nru) * Math.Sqrt(nrv));
|
|
|
|
|
return 1 - similarity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float CosineDistance(byte[] u, byte[] v)
|
|
|
|
|
{
|
|
|
|
|
if (u.Length != v.Length)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Vectors have non-matching dimensions");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float dot = 0.0f;
|
|
|
|
|
float nru = 0.0f;
|
|
|
|
|
float nrv = 0.0f;
|
|
|
|
|
for (int i = 0; i < u.Length; ++i)
|
|
|
|
|
{
|
|
|
|
|
dot += (float)(u[i] * v[i]);
|
|
|
|
|
nru += (float)(u[i] * u[i]);
|
|
|
|
|
nrv += (float)(v[i] * v[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var similarity = dot / (float)(Math.Sqrt(nru) * Math.Sqrt(nrv));
|
|
|
|
|
return 1 - similarity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float CosineDistance(int[] u, int[] v)
|
|
|
|
|
{
|
|
|
|
|
if (u.Length != v.Length)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Vectors have non-matching dimensions");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float dot = 0.0f;
|
|
|
|
|
float nru = 0.0f;
|
|
|
|
|
float nrv = 0.0f;
|
|
|
|
|
byte[] bu;
|
|
|
|
|
byte[] bv;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < u.Length; ++i)
|
|
|
|
|
{
|
|
|
|
|
bu = BitConverter.GetBytes(u[i]);
|
|
|
|
|
bv = BitConverter.GetBytes(v[i]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[0] * bv[0]);
|
|
|
|
|
nru += (float)(bu[0] * bu[0]);
|
|
|
|
|
nrv += (float)(bv[0] * bv[0]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[1] * bv[1]);
|
|
|
|
|
nru += (float)(bu[1] * bu[1]);
|
|
|
|
|
nrv += (float)(bv[1] * bv[1]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[2] * bv[2]);
|
|
|
|
|
nru += (float)(bu[2] * bu[2]);
|
|
|
|
|
nrv += (float)(bv[2] * bv[2]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[3] * bv[3]);
|
|
|
|
|
nru += (float)(bu[3] * bu[3]);
|
|
|
|
|
nrv += (float)(bv[3] * bv[3]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var similarity = dot / (float)(Math.Sqrt(nru) * Math.Sqrt(nrv));
|
|
|
|
|
return 1 - similarity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static float CosineDistance(long[] u, long[] v)
|
|
|
|
|
{
|
|
|
|
|
if (u.Length != v.Length)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Vectors have non-matching dimensions");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float dot = 0.0f;
|
|
|
|
|
float nru = 0.0f;
|
|
|
|
|
float nrv = 0.0f;
|
|
|
|
|
byte[] bu;
|
|
|
|
|
byte[] bv;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < u.Length; ++i)
|
|
|
|
|
{
|
|
|
|
|
bu = BitConverter.GetBytes(u[i]);
|
|
|
|
|
bv = BitConverter.GetBytes(v[i]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[0] * bv[0]);
|
|
|
|
|
nru += (float)(bu[0] * bu[0]);
|
|
|
|
|
nrv += (float)(bv[0] * bv[0]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[1] * bv[1]);
|
|
|
|
|
nru += (float)(bu[1] * bu[1]);
|
|
|
|
|
nrv += (float)(bv[1] * bv[1]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[2] * bv[2]);
|
|
|
|
|
nru += (float)(bu[2] * bu[2]);
|
|
|
|
|
nrv += (float)(bv[2] * bv[2]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[3] * bv[3]);
|
|
|
|
|
nru += (float)(bu[3] * bu[3]);
|
|
|
|
|
nrv += (float)(bv[3] * bv[3]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[4] * bv[4]);
|
|
|
|
|
nru += (float)(bu[4] * bu[4]);
|
|
|
|
|
nrv += (float)(bv[4] * bv[4]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[5] * bv[5]);
|
|
|
|
|
nru += (float)(bu[5] * bu[5]);
|
|
|
|
|
nrv += (float)(bv[5] * bv[5]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[6] * bv[6]);
|
|
|
|
|
nru += (float)(bu[6] * bu[6]);
|
|
|
|
|
nrv += (float)(bv[6] * bv[6]);
|
|
|
|
|
|
|
|
|
|
dot += (float)(bu[7] * bv[7]);
|
|
|
|
|
nru += (float)(bu[7] * bu[7]);
|
|
|
|
|
nrv += (float)(bv[7] * bv[7]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var similarity = dot / (float)(Math.Sqrt(nru) * Math.Sqrt(nrv));
|
|
|
|
|
return 1 - similarity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static double DotProductDistance(float[] e1, float[] e2)
|
|
|
|
|
{
|
|
|
|
|
var sim = 0f;
|
|
|
|
|
for (int i = 0; i < e1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
sim += e1[i] * e2[i];
|
|
|
|
|
}
|
|
|
|
|
return sim;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static double DotProductDistance(byte[] e1, byte[] e2)
|
|
|
|
|
{
|
|
|
|
|
var sim = 0f;
|
|
|
|
|
for (int i = 0; i < e1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
sim += e1[i] * e2[i];
|
|
|
|
|
}
|
|
|
|
|
return sim;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static double DotProductDistance(int[] e1, int[] e2)
|
|
|
|
|
{
|
|
|
|
|
var sim = 0f;
|
|
|
|
|
for (int i = 0; i < e1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
sim += e1[i] * e2[i];
|
|
|
|
|
}
|
|
|
|
|
return sim;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static double DotProductDistance(long[] e1, long[] e2)
|
|
|
|
|
{
|
|
|
|
|
var sim = 0f;
|
|
|
|
|
for (int i = 0; i < e1.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
sim += e1[i] * e2[i];
|
|
|
|
|
}
|
|
|
|
|
return sim;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|