views:

310

answers:

2
+4  A: 

For the first:

static IEnumerable<int> Permute(int x, int y, int z)
{
    yield return x * 100 + y * 10 + z;
    yield return x * 100 + z * 10 + y;
    yield return y * 100 + x * 10 + z;
    yield return y * 100 + z * 10 + x;
    yield return z * 100 + x * 10 + y;
    yield return z * 100 + y * 10 + x;
}
static void Main()
{
    var divs = new[] {3,5,7,11,13,17};
    // combinations of 1-9
    var combinations =
              from x in Enumerable.Range(1, 7)
              from y in Enumerable.Range(x + 1, 8 - x)
              from z in Enumerable.Range(y + 1, 9 - y)
              select new { x, y, z };

    // permute
    var qry = from comb in combinations
              where !Permute(comb.x, comb.y, comb.z).Any(
                i => divs.Any(d => i % d == 0))
              select comb;

    foreach (var answer in qry)
    {
        Console.WriteLine("{0}, {1}, {2}", answer.x, answer.y, answer.z);
    }
}

For the second - not elegant, but it works (returns the 8 permutations of the sample):

static void Main() {
    var data = Enumerable.Range(1, 9);
    var magicSquares =
        // generate 1st row and deduce the target
        from a in data let arrA = new[] { a }
        from b in data.Except(arrA) let arrB = new[] { a,b }
        from c in data.Except(arrB) let arrC = new[] { a,b,c }
        let target = a + b + c
        // generate 2nd row and filter to target matches
        from d in data.Except(arrC) let arrD = new[] { a,b,c,d }
        from e in data.Except(arrD) let arrE = new[] { a,b,c,d,e }
        from f in data.Except(arrE) let arrF = new[] { a,b,c,d,e,f }
        where d + e + f == target 
        // generate 3rd row and filter to target matches
        from g in data.Except(arrF) let arrG = new[] { a,b,c,d,e,f,g }
        from h in data.Except(arrG) let arrH = new[] { a,b,c,d,e,f,g,h }
        from i in data.Except(arrH)
        where g + h + i == target
        // filter columns
           && a + d + g == target
           && b + e + h == target
           && c + f + i == target
        // filter diagonals
           && a + e + i == target
           && c + e + g == target 
       select new {a,b,c,d,e,f,g,h,i};

    foreach (var row in magicSquares)
    {
        Console.WriteLine("{0} {1} {2}", row.a, row.b, row.c);
        Console.WriteLine("{0} {1} {2}", row.d, row.e, row.f);
        Console.WriteLine("{0} {1} {2}", row.g, row.h, row.i);
        Console.WriteLine();
    }
}
Marc Gravell
What about permutations that have two numbers the same? Why aren't you testing 113, 131, 311 for example?
Eric Lippert
(answered on your reply)
Marc Gravell
Ah, you're right, the problem calls out "different digits".
Eric Lippert
+5  A: 

I agree that Marc's solution to your first problem is a reasonable approach. But I think there's a larger question here, which is "how do I solve problems like this in a LINQ-ish manner?"

Notice how your solution is completely "procedural" and "imperative". Your code specifies a series of steps that you would execute, one after the other, with deep loops. Each step along the way is meaningless unless you understand its place in the larger whole.

There are two ideas I like to use when solving problems with LINQ:

  • Describe what the program is doing logically, rather than listing a series of commands
  • Characterize the problem as a query against a data set rather than as a procedure to follow.

So, what's our data set? We wish to filter out some elements from the set of all combinations of three digits.

How do we filter them? Permute the digits and then perform a divisibility check on each permutation.

OK, so now we have a structure for our program:

var query = from c in ThreeDigitCombinations() 
            where DivisibilityCheckPasses(c) 
            select c;
foreach(Combination result in query) Console.WriteLine(result);

And now you can continue breaking down each of those further, solving each sub-problem using LINQ in turn.

Same goes for your "magic square" problem; you're looking for a permutation that has a certain property, so write a generator of permutations, write a filter, and execute it.

Eric Lippert
Re comment; see the "To ensure that there is no ambuigity" comment in the question; it relates to non-repetitive permutations.
Marc Gravell
In particular "say abc,acb,bac,bca,cab and cba will divide by 3,5,7,11,13 or 17."
Marc Gravell