We will treat each name as a bit line of 128 elements. In each record we have two such lines - b [i] and c [i].
First, let's see what happens if for each i we find the sum s [i] of the differences b [i] -c [i] for all the records.
Since all names except the first and last occur in the lines b and c, the same number of times, when summed, they mutually destroy each other, and the sum of the first and last names will remain in the sum. The value of s [i], therefore, can be -1, 0, or 1.
If s [i] = - 1, then the value of b [i] for the first name is 0, and for the second one, if s [i] = 1, then the values ​​will be 1 and 0, respectively. But if s [i] = 0, then we can only say that the values ​​of this bit in the first and last name are the same. How would we find them?
Suppose we know that for some k, our s [k] is nonzero. What happens if we find the XOR values ​​(b [i] & b [k]) ^ (c [i] & c [k])?
For all n names except the first and last, the expression n [i] & n [k] will enter the sum twice (once as b, second time as c) and will give zero contribution. If f is the first name and p is the last, then the sum will remain (f [i] & f [k]) ^ (p [i] & p [k]). We are interested only in those bits for which f [i] = p [i] (we have already found the values ​​of the others). Therefore, (f [i] & f [k]) ^ (p [i] & p [k]) = f [i] & (f [k] ^ p [k]), and since s [k]! = 0 , then f [k] ^ p [k] = 1, and the total sum is equal to f [i].
Unfortunately, we cannot say in advance in which bit the names will differ. Therefore, just in case, we will consider the sums
(b [i] & b [k]) ^ (c [i] & c [k]) for all pairs i, k. In total, we need 128 * 127/2 = 8128 one-bit counters and 128 two-bit counters (to calculate s [i]).
For example, you can write processing as follows (we assume that both names in the record are transmitted in one byte array, written in a row):
static byte[] FindDiffNames(IEnumerable<byte[]> seq) { const int LName=16; byte[,] pairs=new byte[LName*8,LName]; byte[] res=new byte[2*LName]; foreach(byte[] name in seq) { for(int i=0;i<LName;i++) { res[i+LName]^=(byte)(name[i]&res[i]); res[i]^=(byte)(name[i]^name[i+LName]); res[i+LName]^=(byte)(name[i+LName]&res[i]); for(int k=0;k<LName*8;k++) { byte mask=(byte)(1<<(k&7)); if((name[k>>3]&mask)!=0) pairs[k,i]^=name[i]; if((name[LName+(k>>3)]&mask)!=0) pairs[k,i]^=name[i+LName]; } } } for(int i=0;i<LName;i++) { int b0=res[i],b1=res[i+LName],s=0; for(int j=0;j<LName*8;j++) s|=pairs[j,i]; s&=~b0; res[i]=(byte)((b0&~b1)|s); res[i+LName]=(byte)((b0&b1)|s); } return res; }
With this technique, you can also find the difference of sets, one of which is obtained from the other by adding two or even three elements (or adding two and removing one). If the differences are stronger, you have to store the sum of conjunctions not only pairs, but also triples of bits. And the XOR is not enough there - you have to count at least three-bit alternating sums.
UPD: In the discussion of this problem in the comments
SeptiM proposed a simpler solution. We consider names as 128-bit integers (xi, yi), and count the sums S1 = sum (xi-yi), S2 = sum (xi ^ 2-yi ^ 2) (the first sum should be signed 129-bit, the second - sign 257-bit. Overflow is ignored, we work modulo 2 ^ 129 and 2 ^ 257, respectively). It is clear that their values ​​are S1 = x1-xn, S2 = x1 ^ 2-xn ^ 2, where x1 is the first name, xn is the last. From here we easily find x1 = (S1 + S2 / S1) / 2, xn = x1-S1.