Skip to content

Instantly share code, notes, and snippets.

@neilmayhew
Last active August 29, 2015 14:20
Show Gist options
  • Save neilmayhew/3f8dd2264fe38f9bd6db to your computer and use it in GitHub Desktop.
Save neilmayhew/3f8dd2264fe38f9bd6db to your computer and use it in GitHub Desktop.
An example of using C# and LINQ to program in a functional style
// Use the digits 0-9 (once each) to create two numbers by concatenation. (e.g. 152 and 3476980).
// What's the highest product you can achieve when these two numbers are multiplied together?
using System;
using System.Linq;
using System.Numerics;
using System.Collections.Generic;
namespace HighProduct
{
using Number = BigInteger; // Type synonym for the type of numbers we're using
using Split = Partition<int>; // Type synonym for a particular split of the digits
// Extension methods for handling single items as a collection
static class Extensions
{
public static IEnumerable<T> Single<T>(this T item)
{
return Enumerable.Repeat(item, 1);
}
public static IEnumerable<T> Append<T>(this IEnumerable<T> coll, T item)
{
return coll.Concat(Single(item));
}
};
// A generic pair of items of the same type
public class Pair<T>
{
public T left;
public T right;
public Pair(T l, T r)
{
left = l;
right = r;
}
};
// A pair of Numbers representing two factors
public class Factors : Pair<Number>
{
public Factors(Number l, Number r) : base(l, r) {}
public Number product
{
get { return left * right; }
}
};
// A pair of collections representing one possible division of a collection of items
public class Partition<T> : Pair<List<T>>
{
public Partition() : base(new List<T>(), new List<T>()) {}
public Partition(IEnumerable<T> l, IEnumerable<T> r) : base(new List<T>(l), new List<T>(r)) {}
// Derive partitions with the item added to the left and the right
public IEnumerable<Partition<T>> adjoin(T item)
{
return new Partition<T>[] {
new Partition<T>( left.Append(item), right ),
new Partition<T>( left, right.Append(item) )
};
}
// Generate all partitions for a collection of items
public static IEnumerable<Partition<T>> allFor(IEnumerable<T> items)
{
return items.Aggregate(
new Partition<T>().Single(),
(ps, item) => ps.SelectMany(p => p.adjoin(item))
);
}
};
class HighProduct
{
static void Main(string[] args)
{
int radix = Convert.ToInt32(args[0]);
IEnumerable<int> digits = Enumerable.Range(0, radix).Reverse();
IEnumerable<Split> splits = Split.allFor(digits);
IEnumerable<Factors> factors = splits.Select(s => toFactors(s, radix));
Number max = factors.Max(p => p.product);
IEnumerable<Factors> best = factors.Where(p => p.product == max);
foreach (Factors f in best)
{
Console.WriteLine("{0} * {1} = {2}",
ToStringWithRadix(f.left, radix), ToStringWithRadix(f.right, radix), f.left * f.right);
}
}
// Convert a Split of the digits to the equivalent pair of Numbers
public static Factors toFactors(Split s, int radix)
{
return new Factors(toNumber(s.left, radix), toNumber(s.right, radix));
}
// Convert a collection of digits to a Number
public static Number toNumber(IEnumerable<int> digits, int radix)
{
return digits.Aggregate<int, Number>(0, (n, d) => n * radix + d);
}
// Adapted from http://www.pvladov.com/2012/05/decimal-to-arbitrary-numeral-system.html
public static string ToStringWithRadix(Number inputNumber, int radix)
{
const string Digits = "0123456789abcdefghijklmnopqrstuvwxyz";
if (radix < 2 || radix > Digits.Length)
throw new ArgumentException("The radix must be >= 2 and <= " + Digits.Length.ToString());
if (inputNumber == 0)
return "0";
Number currentNumber = Number.Abs(inputNumber);
int maxChars = currentNumber.ToByteArray().Length * 8 + 1;
char[] charArray = new char[maxChars];
int index = maxChars;
while (currentNumber != 0)
{
int remainder = (int)(currentNumber % radix);
charArray[--index] = Digits[remainder];
currentNumber = currentNumber / radix;
}
if (inputNumber < 0)
charArray[--index] = '-';
return new String(charArray, index, maxChars - index);
}
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment