Skip to content

Commit c4e0e1a

Browse files
authored
Merge branch 'master' into feature/string-similarity/OptimalStringAlignment
2 parents 67cbcd5 + 6b37d04 commit c4e0e1a

File tree

8 files changed

+370
-1
lines changed

8 files changed

+370
-1
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System;
2+
using System.Numerics;
3+
using Algorithms.Numeric;
4+
using NUnit.Framework;
5+
6+
namespace Algorithms.Tests.Numeric;
7+
8+
public static class AbsTests
9+
{
10+
[TestCase(0, 0)]
11+
[TestCase(34, 34)]
12+
[TestCase(-100000000000.0d, 100000000000.0d)]
13+
[TestCase(-3, 3)]
14+
[TestCase(-3.1443123d, 3.1443123d)]
15+
public static void GetsAbsVal<T>(T inputNum, T expected) where T : INumber<T>
16+
{
17+
// Act
18+
var result = Abs.AbsVal(inputNum);
19+
20+
// Assert
21+
Assert.That(result, Is.EqualTo(expected));
22+
}
23+
24+
[TestCase(new[] { -3, -1, 2, -11 }, -11)]
25+
[TestCase(new[] { 0, 5, 1, 11 }, 11)]
26+
[TestCase(new[] { 3.0, -10.0, -2.0 }, -10.0d)]
27+
public static void GetAbsMax<T>(T[] inputNums, T expected) where T : INumber<T>
28+
{
29+
// Act
30+
var result = Abs.AbsMax(inputNums);
31+
32+
// Assert
33+
Assert.That(result, Is.EqualTo(expected));
34+
}
35+
36+
[Test]
37+
public static void AbsMaxThrowsArgumentException()
38+
{
39+
// Arrange
40+
var inputNums = Array.Empty<int>();
41+
42+
// Assert
43+
Assert.Throws<ArgumentException>(() => Abs.AbsMax(inputNums));
44+
}
45+
46+
[TestCase(new[] { -3, -1, 2, -11 }, -1)]
47+
[TestCase(new[] { -3, -5, 1, -11 }, 1)]
48+
[TestCase(new[] { 0, 5, 1, 11 }, 0)]
49+
public static void GetAbsMin<T>(T[] inputNums, T expected) where T : INumber<T>
50+
{
51+
// Act
52+
var result = Abs.AbsMin(inputNums);
53+
54+
// Assert
55+
Assert.That(result, Is.EqualTo(expected));
56+
}
57+
58+
[Test]
59+
public static void AbsMinThrowsArgumentException()
60+
{
61+
// Arrange
62+
var inputNums = Array.Empty<int>();
63+
64+
// Assert
65+
Assert.Throws<ArgumentException>(() => Abs.AbsMin(inputNums));
66+
}
67+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using Algorithms.Numeric;
3+
using NUnit.Framework;
4+
5+
namespace Algorithms.Tests.Numeric;
6+
7+
public static class SoftMaxTests
8+
{
9+
[TestCase(new[] {5.0, 5.0}, new[] {0.5, 0.5})]
10+
[TestCase(new[] {1.0, 2.0, 3.0}, new[] {0.09003057317038046, 0.24472847105479767, 0.6652409557748219})]
11+
[TestCase(new[] {0.0}, new[] {1.0})]
12+
public static void SoftMaxFunction(double[] input, double[] expected)
13+
{
14+
// Act
15+
var result = SoftMax.Compute(input);
16+
17+
// Assert
18+
Assert.That(result, Is.EqualTo(expected).Within(1e-9));
19+
}
20+
21+
[Test]
22+
public static void SoftMaxFunctionThrowsArgumentException()
23+
{
24+
// Arrange
25+
var input = Array.Empty<double>();
26+
27+
// Assert
28+
Assert.Throws<ArgumentException>(() => SoftMax.Compute(input));
29+
}
30+
31+
[TestCase(new[] {1.0, 2.0, 3.0, 4.0, 5.0})]
32+
[TestCase(new[] {0.0, 0.0, 0.0, 0.0, 0.0})]
33+
[TestCase(new[] {5.0})]
34+
public static void SoftMaxFunctionSumsToOne(double[] input)
35+
{
36+
// Act
37+
var result = SoftMax.Compute(input);
38+
39+
var sum = 0.0;
40+
foreach (var value in result)
41+
{
42+
sum += value;
43+
}
44+
45+
// Assert
46+
Assert.That(sum, Is.EqualTo(1.0).Within(1e-9));
47+
}
48+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using Algorithms.Strings.PatternMatching;
2+
using NUnit.Framework;
3+
4+
namespace Algorithms.Tests.Strings.PatternMatching;
5+
6+
public static class WildCardMatcherTests
7+
{
8+
[TestCase("aab", "c*a*b", true)]
9+
[TestCase("aaa", "aa", false)]
10+
[TestCase("aaa", "a.a", true)]
11+
[TestCase("aaab", "aa*", false)]
12+
[TestCase("aaab", ".*", true)]
13+
[TestCase("a", "bbbb", false)]
14+
[TestCase("", "bbbb", false)]
15+
[TestCase("a", "", false)]
16+
[TestCase("", "", true)]
17+
public static void MatchPattern(string inputString, string pattern, bool expected)
18+
{
19+
// Act
20+
var result = WildCardMatcher.MatchPattern(inputString, pattern);
21+
22+
// Assert
23+
Assert.That(result, Is.EqualTo(expected));
24+
}
25+
26+
[Test]
27+
public static void MatchPatternThrowsArgumentException()
28+
{
29+
// Arrange
30+
var inputString = "abc";
31+
var pattern = "*abc";
32+
33+
// Assert
34+
Assert.Throws<System.ArgumentException>(() => WildCardMatcher.MatchPattern(inputString, pattern));
35+
}
36+
}

Algorithms/Numeric/Abs.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System;
2+
using System.Numerics;
3+
4+
namespace Algorithms.Numeric;
5+
6+
/// <summary>
7+
/// Find the absolute value of a number.
8+
/// </summary>
9+
public static class Abs
10+
{
11+
/// <summary>
12+
/// Returns the absolute value of a number.
13+
/// </summary>
14+
/// <typeparam name="T">Type of number.</typeparam>
15+
/// <param name="inputNum">Number to find the absolute value of.</param>
16+
/// <returns>Absolute value of the number.</returns>
17+
public static T AbsVal<T>(T inputNum) where T : INumber<T>
18+
{
19+
return T.IsNegative(inputNum) ? -inputNum : inputNum;
20+
}
21+
22+
/// <summary>
23+
/// Returns the number with the smallest absolute value on the input array.
24+
/// </summary>
25+
/// <typeparam name="T">Type of number.</typeparam>
26+
/// <param name="inputNums">Array of numbers to find the smallest absolute.</param>
27+
/// <returns>Smallest absolute number.</returns>
28+
public static T AbsMin<T>(T[] inputNums) where T : INumber<T>
29+
{
30+
if (inputNums.Length == 0)
31+
{
32+
throw new ArgumentException("Array is empty.");
33+
}
34+
35+
var min = inputNums[0];
36+
for (var index = 1; index < inputNums.Length; index++)
37+
{
38+
var current = inputNums[index];
39+
if (AbsVal(current).CompareTo(AbsVal(min)) < 0)
40+
{
41+
min = current;
42+
}
43+
}
44+
45+
return min;
46+
}
47+
48+
/// <summary>
49+
/// Returns the number with the largest absolute value on the input array.
50+
/// </summary>
51+
/// <typeparam name="T">Type of number.</typeparam>
52+
/// <param name="inputNums">Array of numbers to find the largest absolute.</param>
53+
/// <returns>Largest absolute number.</returns>
54+
public static T AbsMax<T>(T[] inputNums) where T : INumber<T>
55+
{
56+
if (inputNums.Length == 0)
57+
{
58+
throw new ArgumentException("Array is empty.");
59+
}
60+
61+
var max = inputNums[0];
62+
for (var index = 1; index < inputNums.Length; index++)
63+
{
64+
var current = inputNums[index];
65+
if (AbsVal(current).CompareTo(AbsVal(max)) > 0)
66+
{
67+
max = current;
68+
}
69+
}
70+
71+
return max;
72+
}
73+
}

Algorithms/Numeric/SoftMax.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
3+
namespace Algorithms.Numeric;
4+
5+
/// <summary>
6+
/// Implementation of the SoftMax function.
7+
/// Its a function that takes as input a vector of K real numbers, and normalizes
8+
/// it into a probability distribution consisting of K probabilities proportional
9+
/// to the exponentials of the input numbers. After softmax, the elements of the vector always sum up to 1.
10+
/// https://en.wikipedia.org/wiki/Softmax_function.
11+
/// </summary>
12+
public static class SoftMax
13+
{
14+
/// <summary>
15+
/// Compute the SoftMax function.
16+
/// The SoftMax function is defined as:
17+
/// softmax(x_i) = exp(x_i) / sum(exp(x_j)) for j = 1 to n
18+
/// where x_i is the i-th element of the input vector.
19+
/// The elements of the output vector are the probabilities of the input vector, the output sums up to 1.
20+
/// </summary>
21+
/// <param name="input">The input vector of real numbers.</param>
22+
/// <returns>The output vector of real numbers.</returns>
23+
public static double[] Compute(double[] input)
24+
{
25+
if (input.Length == 0)
26+
{
27+
throw new ArgumentException("Array is empty.");
28+
}
29+
30+
var exponentVector = new double[input.Length];
31+
var sum = 0.0;
32+
for (var index = 0; index < input.Length; index++)
33+
{
34+
exponentVector[index] = Math.Exp(input[index]);
35+
sum += exponentVector[index];
36+
}
37+
38+
for (var index = 0; index < input.Length; index++)
39+
{
40+
exponentVector[index] /= sum;
41+
}
42+
43+
return exponentVector;
44+
}
45+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using System;
2+
3+
namespace Algorithms.Strings.PatternMatching;
4+
5+
/// <summary>
6+
/// Implentation of regular expression matching with support for '.' and '*'.
7+
/// '.' Matches any single character.
8+
/// '*' Matches zero or more of the preceding element.
9+
/// The matching should cover the entire input string (not partial).
10+
/// </summary>
11+
public static class WildCardMatcher
12+
{
13+
/// <summary>
14+
/// Using bottom-up dynamic programming for matching the input string with the pattern.
15+
///
16+
/// Time complexity: O(n*m), where n is the length of the input string and m is the length of the pattern.
17+
///
18+
/// Constrain: The pattern cannot start with '*'.
19+
/// </summary>
20+
/// <param name="inputString">The input string to match.</param>
21+
/// <param name="pattern">The pattern to match.</param>
22+
/// <returns>True if the input string matches the pattern, false otherwise.</returns>
23+
/// <exception cref="ArgumentException">Thrown when the pattern starts with '*'.</exception>
24+
public static bool MatchPattern(string inputString, string pattern)
25+
{
26+
if (pattern.Length > 0 && pattern[0] == '*')
27+
{
28+
throw new ArgumentException("Pattern cannot start with *");
29+
}
30+
31+
var inputLength = inputString.Length + 1;
32+
var patternLength = pattern.Length + 1;
33+
34+
// DP 2d matrix, where dp[i, j] is true if the first i characters in the input string match the first j characters in the pattern
35+
// This DP is initialized to all falses, as it is the default value for a boolean.
36+
var dp = new bool[inputLength, patternLength];
37+
38+
// Empty string and empty pattern are a match
39+
dp[0, 0] = true;
40+
41+
// Since the empty string can only match a pattern that has a * in it, we need to initialize the first row of the DP matrix
42+
for (var j = 1; j < patternLength; j++)
43+
{
44+
if (pattern[j - 1] == '*')
45+
{
46+
dp[0, j] = dp[0, j - 2];
47+
}
48+
}
49+
50+
// Now using bottom-up approach to find for all remaining lenghts of input and pattern
51+
for (var i = 1; i < inputLength; i++)
52+
{
53+
for (var j = 1; j < patternLength; j++)
54+
{
55+
MatchRemainingLenghts(inputString, pattern, dp, i, j);
56+
}
57+
}
58+
59+
return dp[inputLength - 1, patternLength - 1];
60+
}
61+
62+
// Helper method to match the remaining lengths of the input string and the pattern
63+
// This method is called for all i and j where i > 0 and j > 0
64+
private static void MatchRemainingLenghts(string inputString, string pattern, bool[,] dp, int i, int j)
65+
{
66+
// If the characters match or the pattern has a ., then the result is the same as the previous positions.
67+
if (inputString[i - 1] == pattern[j - 1] || pattern[j - 1] == '.')
68+
{
69+
dp[i, j] = dp[i - 1, j - 1];
70+
}
71+
else if (pattern[j - 1] == '*')
72+
{
73+
MatchForZeroOrMore(inputString, pattern, dp, i, j);
74+
}
75+
else
76+
{
77+
// If the characters do not match, then the result is false, which is the default value.
78+
}
79+
}
80+
81+
// Helper method to match for the "*" pattern.
82+
private static void MatchForZeroOrMore(string inputString, string pattern, bool[,] dp, int i, int j)
83+
{
84+
if (dp[i, j - 2])
85+
{
86+
dp[i, j] = true;
87+
}
88+
else if (inputString[i - 1] == pattern[j - 2] || pattern[j - 2] == '.')
89+
{
90+
dp[i, j] = dp[i - 1, j];
91+
}
92+
else
93+
{
94+
// Leave the default value of false
95+
}
96+
}
97+
}

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ this repository.
66

77
We welcome adding new algorithms and data structures that were mentioned in books or other reputable sources.
88
We also welcome fixing bugs in code, clarifying documentation and adding new test cases to check existing code.
9-
The framework targeted by our code is **dotnet 6**. The corresponding SDK can be found [here](https://dotnet.microsoft.com/download/dotnet/6.0).
9+
The framework targeted by our code is **dotnet 8**. The corresponding SDK can be found [here](https://dotnet.microsoft.com/download/dotnet/8.0).
1010

1111
Please note that we have a code of conduct, please follow it in all your interactions with the project.
1212

0 commit comments

Comments
 (0)