Generally, you don't want to use loops to implement complicated iterations / recursive functions in F#. That's what tail recursive functions are good for.
To get the computational complexity below O(n²), how about this: consider the set of candidate summands, which is the complete set of numbers at first. Now look at the sum of the largest and smallest number in this set. If this sum is larger than the target number, the largest number won't sum to the target number with anything. The same goes the other way around.
Keep dropping the smallest or largest number from the set of candidates until either the set is empty or a match has been found.
The code could look like this:
let rec f M x =
if Set.isEmpty M then false else
let biggest, smallest = Set.maxElement M, Set.minElement M
let sum = biggest + smallest
if sum > x then f (Set.remove biggest M) x
elif sum < x then f (Set.remove smallest M) x
elif sum = x then true
else failwith "Encountered failure to compare numbers / NaN"
When I look at the mathematical definition, it doesn't seem to be a requirement that the target number is in the set. But if this matters, just check beforehand:
let fChecked M x =
if Set.contains x M then f M x
else invalidArg "x" "Target number is not contained in set."
Tom make this generic over the comparable type, the function could be marked inline
.
Set operations are O(log n), we go over the whole thing once, multiplying O(n), making the total complexity O(n log n) (where n is the number of elements in the set).
Effective speed version
If it's about actual speed rather than "just" the computational complexity, arrays are probably faster than immutable sets. Here's a version that uses array indices:
/// Version where M is types as an unsorted array we're allowed to sort
let f M x =
Array.sortInPlace M
let rec iter iLow iHigh =
if iLow > iHigh then false else
let sum = M.[iLow] + M.[iHigh]
if sum > x then iter iLow (iHigh - 1)
elif sum < x then iter (iLow + 1) iHigh
elif sum = x then true
else failwith "Encountered failure to compare numbers / NaN"
iter 0 (M.Length - 1)
Note that we could pre-sort the array if the same set is used repeatedly, bringing down the complexity for each call on the same set down to O(n).