Home

June 13, 2017: fixed an overflow error when calculating the total in the C# version; some minor typos fixed.

The code in these projects counts sum free sets.

"A sum-free set S is a set for which the intersection of S and the 
sumset S+S is empty." 

http://mathworld.wolfram.com/Sum-FreeSet.html

For example, the set of all odd numbers is sum free. {1, 2} is not sum 
free because 1+1=2.

The novel aspect of this application is avoiding sums on 
individual elements of sets. Instead, the set is represented by its
characteristic identity (each bit is one element); the associated sums 
of a set are also stored in a characteristic identity. Determining
whether an element can be added to a set is then equivalent to testing
if a bit is set. Adding an element to a set is slightly more involved,
but still much faster than computing sums on every combination of elements.

-----

Why does this work? Consider the characteristic identity of a set.

sfs = 00000101011010101101011 ... 

If one is a member, then a fast check for inconsistency is 

  ((sfs | (sfs >> 1)) > 0)

That is, elements that differ by one.

If two is a member, then a fast check for inconsistency is 

  ((sfs | (sfs >> 2)) > 0)

That is, elements that differ by two.
etc.

In fact, when adding an element to a set these potential inconsistencies
are calculated (based on set membership). This is referred to as the "cover"
as it covers any bit positions that can not be added. It turns out adding
a new member is as easy as 1) testing whether the bit is unset in the cover
then 2) extending the existing cover.

The entire catalog of known sum free sets is stored in memory. Extending
"out" to a higher number is equivalent to adding an extra bit (based on the
algorithm above) to every known set. Erdős proved that the number 
of sum free sets is

2^(1/2 + o(1))n

and in fact, extending two extra numbers (bits) approximately doubles
the amount of memory used.

-----

I had hoped to extend the known values a bit higher, but resource
use is rather high. A007865(52) uses approximately 30 GB of RAM (C# version).

I was, perhaps, rather optimistic and forked the C# version to a c++ 
version. The C# version uses UInt64 as the data type to store characteristic 
identities (can compute sets 1..64). The c++ version uses a 
custom 128 bit data type, which is simply two uint64_t's. (I did consider 
an arbitrary precision implementation such as with GMP but decided on 
simplicity, but perhaps that would have been better optimized ... ). The c++
version is a small amount faster but uses much more memory. 

Once RAM is exhausted, higher counts can be achieved by computing 
set membership sequentially (see EXTEND_MAX_NUMBER_BY). This is 
expensive, and grows at a factor of 2^n.

Other than the differences in primary data type the C# version and c++
version should yield the same results, and the code is identical 
where possible. I have used a traditional c naming convention
across both files for the sake of consistency... 

The C# program, at least when compiled with Visual Studio 2017,
requires an x64 target and large objects. Make sure this is
in the app.config:

    <runtime>
        <gcAllowVeryLargeObjects enabled="true" />
    </runtime>

-----

The last thing worth mentioning is a minor detour I explored while it was 
convenient: Count the number of ways elements in the interval [1,m] can 
be partitioned into k sum free sets. This is commented out in the 
main loop (CalculatePartitionNumber). The counts will yield the 
Schur numbers (http://mathworld.wolfram.com/SchurNumber.html) but 
getting there is achieved via recursion, and becomes
intractable very quickly.

-----

gmail me at benjaminaburns

Download:

c# version

c++ version

zip with everything, release notes, c# solution