2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,23 +1,49 @@
[[File:Paraffins.isopentane.png|450px||right]]
This organic chemistry task is essentially to implement a tree enumeration algorithm.
The problem is to enumerate, without repetitions and in order of increasing size, all possible paraffin molecules (or [[wp:alkane|alkane]]s). Paraffins are built up using only carbon, which has 4 bonds and hydrogen, which has 1. All bonds for each atom must be used, so it is easiest to think of an alkane as linked carbon atoms forming the "backbone" structure, with adding hydrogens linking the remaining unused bonds.
In a paraffin one is allowed neither double bonds (two bonds between the same pair of atoms) nor cycles of linked carbons, so all paraffins with <em>n</em> carbon atoms share the empirical formula C<sub>n</sub>H<sub>2n+2</sub> but for all n >= 4 there are several distinct molecules ("isomers") with the same formula but different structures. The number of isomers rises rather rapidly with n. In counting isomers it should be borne in mind that the four bond positions on a given carbon atom can be freely interchanged and bonds rotated (including 3-D "out of the paper" rotations when you are looking at a flat diagram), so rotations or reorientations of parts of the molecule (without breaking bonds) do not give different isomers. So what seem at first to be different molecules may in fact turn out to be different orientations of the same molecule.
;Task:
Enumerate, without repetitions and in order of increasing size, all possible paraffin molecules (also known as [[wp:alkane|alkane]]s).
For example with n = 3 there is only 1 way of linking the carbons despite the different orientations you can draw the molecule in; and with n = 4 there are 2 configurations, a straight chain: (CH<sub>3</sub>)(CH<sub>2</sub>)(CH<sub>2</sub>)(CH<sub>3</sub>) and a branched chain: (CH<sub>3</sub>)(CH(CH<sub>3</sub>))(CH<sub>3</sub>). Due to bond rotations it doesn't matter which direction the branch points in. The phenomenon of "stereo-isomerism" (a molecule being different from its mirror image due to the actual 3-D arrangement of bonds) is ignored for the purpose of this task.
The input is just the number 'n' of carbon atoms of a molecule, like 17. The output is how many different different paraffins there are with 'n' carbon atoms (like 24_894 if n = 17).
Paraffins are built up using only carbon atoms, which has four bonds, and hydrogen, which has one bond. All bonds for each atom must be used, so it is easiest to think of an alkane as linked carbon atoms forming the "backbone" structure, with adding hydrogen atoms linking the remaining unused bonds.
The sequence of those results is visible in the [[oeis:A000602|Sloane encyclopedia]]. The sequence is (the index starts from 0, and represents the number of carbon atoms):
In a paraffin, one is allowed neither double bonds (two bonds between the same pair of atoms), nor cycles of linked carbons. So all paraffins with &nbsp; '''n''' &nbsp; carbon atoms share the empirical formula &nbsp; <big>C<sub>n</sub>H<sub>2n+2</sub></big>
But for all &nbsp; '''n''' ≥ 4 &nbsp; there are several distinct molecules ("isomers") with the same formula but different structures.
The number of isomers rises rather rapidly when &nbsp; '''n''' &nbsp; increases.
In counting isomers it should be borne in mind that the four bond positions on a given carbon atom can be freely interchanged and bonds rotated (including 3-D "out of the paper" rotations when it's being observed on a flat diagram), so rotations or re-orientations of parts of the molecule (without breaking bonds) do not give different isomers. So what seem at first to be different molecules may in fact turn out to be different orientations of the same molecule.
;Example:
With &nbsp; '''n''' = 3 &nbsp; there is only one way of linking the carbons despite the different orientations the molecule can be drawn; &nbsp; and with &nbsp; '''n''' = 4 &nbsp; there are two configurations:
:::* a &nbsp; straight chain: &nbsp; &nbsp; <big>(CH<sub>3</sub>)(CH<sub>2</sub>)(CH<sub>2</sub>)(CH<sub>3</sub>)</big>
:::* a branched chain: &nbsp; &nbsp; <big>(CH<sub>3</sub>)(CH(CH<sub>3</sub>))(CH<sub>3</sub>)</big>
<br>
Due to bond rotations, it doesn't matter which direction the branch points in.
The phenomenon of "stereo-isomerism" (a molecule being different from its mirror image due to the actual 3-D arrangement of bonds) is ignored for the purpose of this task.
The input is the number &nbsp; '''n''' &nbsp; of carbon atoms of a molecule (for instance '''17''').
The output is how many different different paraffins there are with &nbsp; '''n''' &nbsp; carbon atoms (for instance &nbsp; 24,894 &nbsp; if &nbsp; '''n''' = 17).
The sequence of those results is visible in the [[oeis:A000602|Sloane encyclopedia]]. The sequence is (the index starts from zero, and represents the number of carbon atoms):
1, 1, 1, 1, 2, 3, 5, 9, 18, 35, 75, 159, 355, 802, 1858, 4347, 10359,
24894, 60523, 148284, 366319, 910726, 2278658, 5731580, 14490245,
36797588, 93839412, 240215803, 617105614, 1590507121, 4111846763,
10660307791, 27711253769, ...
'''Extra credit'''
Show the paraffins in some way. A flat 1D representation, with arrays or lists is enough, like:
;Extra credit:
Show the paraffins in some way.
A flat 1D representation, with arrays or lists is enough, for instance:
<lang haskell>*Main> all_paraffins 1
[CCP H H H H]
@ -39,8 +65,8 @@ Show the paraffins in some way. A flat 1D representation, with arrays or lists i
(C H H (C H H H)) (C H H (C H H H)),CCP (C H H H) (C H H H)
(C H H H) (C H H (C H H H))]</lang>
Showing a basic 2D ASCII-art representation of the paraffines is better, like (molecule names aren't necessary):
<pre> Methane Ethane Propane Iso-butane
Showing a basic 2D ASCII-art representation of the paraffins is better; for instance (molecule names aren't necessary):
<pre> Methane Ethane Propane Isobutane
H H H H H H H H H
| | | | | | | | |
@ -52,16 +78,17 @@ H - C - H H - C - C - H H - C - C - C - H H - C - C - C - H
|
H</pre>
'''Links'''
A paper that explains the problem and its solution in a functional language:
;Links:
* A paper that explains the problem and its solution in a functional language:
http://www.cs.wright.edu/~tkprasad/courses/cs776/paraffins-turner.pdf
A Haskell implementation:
http://darcs.brianweb.net/nofib/imaginary/paraffins/Main.hs &nbsp; ◄── dead link.
* A Haskell implementation:
https://github.com/ghc/nofib/blob/master/imaginary/paraffins/Main.hs
A Scheme implementation:
* A Scheme implementation:
http://www.ccs.neu.edu/home/will/Twobit/src/paraffins.scm
A Fortress implementation:
* A Fortress implementation:
http://java.net/projects/projectfortress/sources/sources/content/ProjectFortress/demos/turnersParaffins0.fss?rev=3005
<br><br>

View file

@ -0,0 +1,59 @@
import java.math.BigInteger;
import java.util.Arrays;
class Test {
final static int nMax = 250;
final static int nBranches = 4;
static BigInteger[] rooted = new BigInteger[nMax + 1];
static BigInteger[] unrooted = new BigInteger[nMax + 1];
static BigInteger[] c = new BigInteger[nBranches];
static void tree(int br, int n, int l, int inSum, BigInteger cnt) {
int sum = inSum;
for (int b = br + 1; b <= nBranches; b++) {
sum += n;
if (sum > nMax || (l * 2 >= sum && b >= nBranches))
return;
BigInteger tmp = rooted[n];
if (b == br + 1) {
c[br] = tmp.multiply(cnt);
} else {
c[br] = c[br].multiply(tmp.add(BigInteger.valueOf(b - br - 1)));
c[br] = c[br].divide(BigInteger.valueOf(b - br));
}
if (l * 2 < sum)
unrooted[sum] = unrooted[sum].add(c[br]);
if (b < nBranches)
rooted[sum] = rooted[sum].add(c[br]);
for (int m = n - 1; m > 0; m--)
tree(b, m, l, sum, c[br]);
}
}
static void bicenter(int s) {
if ((s & 1) == 0) {
BigInteger tmp = rooted[s / 2];
tmp = tmp.add(BigInteger.ONE).multiply(rooted[s / 2]);
unrooted[s] = unrooted[s].add(tmp.shiftRight(1));
}
}
public static void main(String[] args) {
Arrays.fill(rooted, BigInteger.ZERO);
Arrays.fill(unrooted, BigInteger.ZERO);
rooted[0] = rooted[1] = BigInteger.ONE;
unrooted[0] = unrooted[1] = BigInteger.ONE;
for (int n = 1; n <= nMax; n++) {
tree(0, n, n, 1, BigInteger.ONE);
bicenter(n);
System.out.printf("%d: %s%n", n, unrooted[n]);
}
}
}

View file

@ -1,6 +1,6 @@
sub count-unrooted-trees(Int $max-branches, Int $max-weight) {
my @rooted = 1,1,0 xx $max-weight - 1;
my @unrooted = 1,1,0 xx $max-weight - 1;
my @rooted = flat 1,1,0 xx $max-weight - 1;
my @unrooted = flat 1,1,0 xx $max-weight - 1;
sub count-trees-with-centroid(Int $radius) {
sub add-branches(
@ -41,5 +41,5 @@ sub count-unrooted-trees(Int $max-branches, Int $max-weight) {
}
my constant N = 100;
my @paraffins := count-unrooted-trees(4, N);
say .fmt('%3d'), ': ', @paraffins[$_] for 1 .. 30, N;
my @paraffins = count-unrooted-trees(4, N);
say .fmt('%3d'), ': ', @paraffins[$_] for flat 1 .. 30, N;

View file

@ -1,27 +1,27 @@
/*REXX program to enumerate number # paraffins for N atoms of carbon.*/
parse arg nodes .; if nodes=='' then nodes=100 /*Not given? Use default*/
rooted. = 0; rooted.0=1; rooted.1=1 /*define base rooted #s.*/
unrooted. = 0; unrooted.0=1; unrooted.1=1 /* " " unrooted " */
numeric digits max(9,nodes%2) /*may use gi-hugeic nums*/
w=length(nodes) /*for formatted display.*/
say right(0,w) unrooted.0 /*··· zero carbon atoms.*/
/* [↓] process nodes. */
do C=1 for nodes; h=C%2 /*C: # of carbon atoms.*/
call tree 0, C, C, 1, 1 /* [↓] if C is even. */
if C//2==0 then unrooted.C=unrooted.C + rooted.h*(rooted.h+1)%2
say right(C,w) unrooted.C /*display formatted #'s.*/
/*REXX program enumerates (without repetition) the # of paraffins with N atoms of carbon*/
parse arg nodes . /*obtain optional argument from the CL.*/
if nodes=='' | nodes=="," then nodes=100 /*Not specified? Then use the default.*/
rooted. = 0; rooted.0=1; rooted.1=1 /*define the base rooted numbers.*/
unrooted. = 0; unrooted.0=1; unrooted.1=1 /* " " " unrooted " */
numeric digits max(9,nodes%2) /*this program may use gihugeic numbers*/
w=length(nodes) /*W: used for aligning formatted nodes.*/
say right(0,w) unrooted.0 /*show enumerations of 0 carbon atoms*/
/* [↓] process all nodes (up to NODES)*/
do C=1 for nodes; h=C%2 /*C: is the number of carbon atoms. */
call tree 0, C, C, 1, 1 /* [↓] if # of carbon atoms is even···*/
if C//2==0 then unrooted.C=unrooted.C + rooted.h * (rooted.h + 1) % 2
say right(C,w) unrooted.C /*display an aligned formatted number. */
end /*C*/
exit /*stick a fork in it, we're done.*/
/*──────────────────────────────────TREE subroutine─────────────────────*/
tree: procedure expose rooted. unrooted. nodes #. /*recursive.*/
parse arg br,n,L,sum,cnt; nm=n-1; LL=L+L; brp=br+1
do b=brp to 4; sum=sum+n; if sum>nodes then leave
if b==4 then if LL>=sum then leave
if b==brp then #.br=rooted.n*cnt
else #.br=#.br*(rooted.n+b-brp)%(b-br)
if LL<sum then unrooted.sum=unrooted.sum+#.br
if b==4 then leave
rooted.sum = rooted.sum+#.br
do m=nm by -1 for nm; call tree b,m,L,sum,#.br; end /*m*/
end /*b*/
return
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
tree: procedure expose rooted. unrooted. nodes #. /*this function is recursive.*/
parse arg br,n,L,sum,cnt; nm=n-1; LL=L+L; brp=br+1
do b=brp to 4; sum=sum+n; if sum>nodes then leave
if b==4 then if LL>=sum then leave
if b==brp then #.br=rooted.n * cnt
else #.br=#.br * (rooted.n + b - brp) % (b - br)
if LL<sum then unrooted.sum=unrooted.sum + #.br
if b==4 then leave
rooted.sum = rooted.sum + #.br
do m=nm by -1 for nm; call tree b, m, L, sum, #.br; end /*m*/
end /*b*/ /* ↑↑↑↑↑↑↑↑↑ recursive invocation of TREE. */