16
\$\begingroup\$

Your task is to accept as input two gene sequences, and a sequence of "cross over points", and return the gene sequence that results from the indicated cross overs.

What I mean by this is, say you have the sequences [A, A, A, A, A, A, A] and [Z, Z, Z, Z, Z, Z, Z], and cross over points of 2 and 5. The resulting sequence would be [A, A, Z, Z, Z, A, A], because:

Cross Here:   V     V
Indices:  0 1 2 3 4 5 6

Genes 1:  A A A A A A A
Genes 2:  Z Z Z Z Z Z Z

Result:   A A Z Z Z A A
              ^     ^

Note that while I'm using letters here for clarity, the actual challenge uses numbers for genes.

The result is the first sequence until a cross over point is encountered, then the result takes from the second sequence until another cross over point is encountered, then the result takes from the first sequence until a cross over point is encountered...

Input:

  • Input can be any reasonable form. The two sequences can be a pair, with the points as the second argument, all three can be separate arguments, a single triplet of (genes 1, genes 2, cross-points), a map with named keys...

  • The cross points will always be in order, and will always be inbounds. There won't be duplicate points, but the list of cross over points may be empty.

  • Gene sequences will always be the same length, and will be non-empty.

  • Indices can be 0 or 1 based.

  • Genes will always be numbers in the range 0-255.

  • It doesn't matter which argument is "genes 1" or "genes 2". In the case of no cross over points, the result can either be either entirely "genes 1" or "genes 2".


Output

  • Output can be any reasonable form that isn't ambiguous. It can be a array/list of numbers, an array of string numbers, a delimited string of numbers (some non-numeric character must separate the numbers)...

  • It can be returned or printed to the std-out.


Entries can by full programs or functions.


Test Cases (genes 1, genes 2, cross points) => result:

[0], [1], [0] => [1]
[0, 1], [9, 8], [1] => [0, 8]
[0, 2, 4, 6, 8, 0], [1, 3, 5, 7, 9, 1], [1, 3, 5] => [0, 3, 5, 6, 8, 1]
[1, 2, 3, 4], [5, 6, 7, 8], [] => [1, 2, 3, 4]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 2, 3, 6, 8] => [1, 1, 0, 1, 1, 1, 0, 0, 1, 1]

This is Code Golf.

\$\endgroup\$
  • \$\begingroup\$ Your worked example would be a bit clearer if the crossover indices weren't also elements in the sequences. \$\endgroup\$ – Shaggy Mar 24 '18 at 23:12
  • 1
    \$\begingroup\$ Fixed. Changed it to A's and Z's. Hope that's clearer. \$\endgroup\$ – Carcigenicate Mar 24 '18 at 23:22

13 Answers 13

1
\$\begingroup\$

Jelly, 12 10 bytes

ṁ⁹L‘¤ḣ"ḷ"/

Try it online!

Argument 1: seq1, seq2
Argument 2: cross points (0-indexed)

\$\endgroup\$
  • \$\begingroup\$ There was a reason... this does not work for one of the test cases! \$\endgroup\$ – Jonathan Allan Mar 24 '18 at 23:56
  • \$\begingroup\$ Also fails in other scenarios, e.g. \$\endgroup\$ – Jonathan Allan Mar 25 '18 at 0:01
  • \$\begingroup\$ Looks like something like ;⁹ZL‘¤Ṭ+\ịŒDḢ would be required :( \$\endgroup\$ – Jonathan Allan Mar 25 '18 at 0:11
  • \$\begingroup\$ @JonathanAllan I actually managed to find a 12-byte version quite different than what you suggested. :) \$\endgroup\$ – Erik the Outgolfer Mar 25 '18 at 10:37
  • \$\begingroup\$ @JonathanAllan ...and then I discovered a completely different 10-byte version, checked with both your links and another test case (relax, I did remember to change to 0-based indexing). :D \$\endgroup\$ – Erik the Outgolfer Mar 25 '18 at 11:50
4
\$\begingroup\$

Haskell, 58 53 51 45 bytes

(fst.).foldl(\(a,b)p->(take p a++drop p b,a))

The two gene sequences are taken as a pair of lists and the cross points as a second argument.

Try it online!

foldl           -- fold the pair of genes into the list of
                -- cross points and on each step
    \(a,b) p -> -- let the pair of genes be (a,b) and the next cross point 'p'
      (take p a++drop p b,a)  
                -- let 'b' the new first element of the pair, but
                --   drop the first 'p' elements and 
                --   prepend the first 'p' elements of 'a'
                -- let 'a' the new second element 
fst             -- when finished, return the first gene   
\$\endgroup\$
4
\$\begingroup\$

JavaScript (ES6), 47 45 bytes

Saved 2 bytes thanks to @ETHproductions

Takes input as a triplet [a, b, c] where a and b are the gene sequences and c is the list of 0-indexed cross-points.

x=>x[i=j=0].map(_=>x[(j+=x[2][j]==i)&1][i++])

Try it online!

Commented

x =>                    // given x = [ geneSeqA, geneSeqB, crossPoints ]
  x[i = j = 0]          // initialize i = gene sequence pointer and j = cross point pointer
  .map(_ =>             // for each value in the first gene sequence:
    x[(                 //   access x[]
      j += x[2][j] == i //     increment j if i is equal to the next cross point
    ) & 1]              //   access either x[0] or x[1] according to the parity of j
    [i++]               //   read gene at x[0][i] or x[1][i]; increment i
  )                     // end of map()
\$\endgroup\$
  • \$\begingroup\$ I believe you can do something like x[(j+=x[2][j]==i)%2][i++] to save a couple of bytes. \$\endgroup\$ – ETHproductions Mar 25 '18 at 2:51
  • \$\begingroup\$ @ETHproductions Thanks! I foolishly tried to add a 3rd variable to keep track of the pointer in x[2] but overlooked this optimization. \$\endgroup\$ – Arnauld Mar 25 '18 at 10:41
3
\$\begingroup\$

APL (Dyalog 16.0), 26 bytes

+/a⎕×(~,⊢)⊂≠\d←1@⎕⊢0⍴⍨≢a←⎕

Try it online!

Input is a, c, then b. c is 1 indexed.

How?

a←⎕ - get a.

0⍴⍨≢ - create array of 0s at its length.

1@⎕⊢ - take c and change the 0s to 1s on the indices.

d← - assign to d.

⊂≠\d - expand d with xor to create the selection sequence (0 for a, 1 for b), and enclose.

(~,⊢) - take d and its inverse.

a⎕× - and multiply respectively with inputted b and a.

+/ - sum each pair of elements, yielding the as on 0s and bs on 1s.

\$\endgroup\$
  • \$\begingroup\$ ⊢0⍴⍨≢ -> ≠⍨ (tip) \$\endgroup\$ – ngn Mar 25 '18 at 3:16
  • \$\begingroup\$ @ngn I can't get it to work [tio] \$\endgroup\$ – Uriel Mar 25 '18 at 8:59
  • \$\begingroup\$ you need a , before 1-element vectors in the input \$\endgroup\$ – ngn Mar 25 '18 at 14:36
3
\$\begingroup\$

Python 2, 43 bytes

def f(a,b,l):
 for p in l:a[p:],b=b[p:],a*1

Try it online!

Outputs by modifying the argument a. Instead as a program:

50 bytes

a,b,l=input()
for p in l:a[p:],b=b[p:],a*1
print a

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Perl 5 -a, 45 40 bytes

Give input in the order "control", "second sequence", "first sequence" as separate lines on STDIN

#!/usr/bin/perl -alp
@{$.}=@F}for(map${$.^=$%~~@1}[$%++],@2){

Try it online!

\$\endgroup\$
2
\$\begingroup\$

J, 24 bytes

4 :'(2|+/\1 x}I.#{.y)}y'

Try it online!

I don't count the f=: chars, because it works equally well as an anonymous function (as demonstrated in a TIO sample)

Note: It doesn't work for empty list of cross over points!

An explicit oneliner, x is the left argument - the list of cross over points, y is the right argument, a two-row table of the sequences.

Explanation:

4 :' ... ' - a dyadic verb

(...)}y - Each atom of operand (...) selects an atom from the corresponding positions of the items of y

#{.y - takes the first sequence and find its length

    #{. 0 2 4 6 8 0,: 1 3 5 7 9 1
6

I. creates a list of zeros with length the argument

   I.6
0 0 0 0 0 0

1 x} changes the items of the rigth argument (a list of zeroes) to 1 at indices indicated by x (the list of cors over points)

   1(1 3 5)}I.6
0 1 0 1 0 1

+/\ running sums of a list

   +/\ 0 1 0 1 0 1
0 1 1 2 2 3

2| modulo 2

   2|+/\ 0 1 0 1 0 1
0 1 1 0 0 1

Assembled:

    0 1 1 0 0 1 } 0 2 4 6 8 0 ,: 1 3 5 7 9 1
0 3 5 6 8 1
\$\endgroup\$
2
\$\begingroup\$

R, 84 79 bytes

function(G,K){o=G[,1]
m=1:nrow(G)
for(i in K)o[m>=i]=G[m>=i,match(i,K)%%2+1]
o}

Try it online!

Takes input as a matrix of 2 columns and a vector.

\$\endgroup\$
2
\$\begingroup\$

Python 3, 61 60 bytes

f=lambda a,b,c,d=0:c and a[d:c[0]]+f(b,a,c[1:],c[0])or a[d:]

Try it online!

-1 byte from Jonathan Frech

Explanation:

f=lambda a,b,c,d=0:c and a[d:c[0]]+f(b,a,c[1:],c[0])or a[d:]
f=lambda a,b,c,d=0:
 # recursive lambda: a and b are the two lists,
 # c is the crossovers, and d is where to start
                   c and
 # if there is at least one crossover left
 #  then
                         a[d:c[0]]
 #  return the items of the first list from the
 #  starting point up to the first crossover
                                  +f(b,a,c[1:],c[0])
 #  plus the result of the inverted lists with
 #  the remaining crossovers, starting where
 #  the first part left off
                                                    or
 # else
                                                       a[d:]
 #  the first list from the starting point to the end
\$\endgroup\$
  • 1
    \$\begingroup\$ Possible 60 bytes; assuming that a[d:c[0]]+f(b,a,c[1:],c[0]) will never be false. \$\endgroup\$ – Jonathan Frech Mar 25 '18 at 13:30
1
\$\begingroup\$

Jelly, 13 bytes

ṬœṗЀż/JḂị"ƊF

A dyadic link accepting the (1-indexed) crossover points on the left and a list of the two sequences on the right which returns the resulting list.

Try it online!

How?

ṬœṗЀż/JḂị"ƊF - Link: list, C; list, S     e.g. [2,4,6]; [[0,2,4,6,8,0],[1,3,5,7,9,1]]
Ṭ             - untruth C                       [0,1,0,1,0,1]
   Ѐ         - map across S with:
 œṗ           -   partition at truthy indices   [[0],[2,4],[6,8],[0]]  /  [[1],[3,5],[7,9],[1]]
      /       - reduce with:
     ż        -   zip                           [[[0],[1]],[[2,4],[3,5]],[[6,8],[7,9]],[[0],[1]]]
           Ɗ  - last three links as a monad:
       J      -   range of length               [1,2,3,4]
        Ḃ     -   bit (modulo by 2)             [1,0,1,0]
          "   -   zip with:
         ị    -     index into                  [[0],[3,5],[6,8],[1]]
            F - flatten                         [0,3,5,6,8,1]
\$\endgroup\$
  • \$\begingroup\$ @Carcigenicate - thanks I just noticed after asking :D \$\endgroup\$ – Jonathan Allan Mar 24 '18 at 23:18
  • \$\begingroup\$ : What a useless thing for indexing into a 2-element list. ż/: How useless of a complication, it's cruelly flattened away by a big truck anyway! \$\endgroup\$ – Erik the Outgolfer Mar 24 '18 at 23:46
1
\$\begingroup\$

Charcoal, 19 bytes

AθAηE§θ⁰§§θLΦ⊕κ№ηλκ

Try it online! Link is to verbose version of code. Takes input as a pair of string gene sequences and a 0-indexed list of crossing points. Explanation:

Aθ                  Input the pair of gene sequences into `q`
  Aη                Input the list of crossing points into `h`
    E§θ⁰            Loop over one of the gene sequences
              κ     Current index
             ⊕      Incremented
            Φ  №ηλ  Intersect implicit range with crossing points
           L        Take the length
         §θ         Cyclically index into the pair of gene sequences
        §         κ Take the appropriate element of that sequence
                    Implicitly output on separate lines

Alternatively, could be subsituted for to print the result as a string. Try it online!

\$\endgroup\$
1
\$\begingroup\$

SWI-Prolog, 78 bytes

A/B/[0|C]/D:-B/A/C/D. [H|A]/[_|B]/C/[H|D]:-maplist(succ,E,C),A/B/E/D. A/_/_/A.

Usage: Call "Genes1/Genes2/CrossoverPoints/X" where "Genes1", "Genes2", "CrossoverPoints" are bracket-enclosed, comma-separated lists.

\$\endgroup\$
1
\$\begingroup\$

C (clang), 79 bytes

*g[2],*c,l,m;f(i,j,k){for(i=j=k=0;i<l;g[0][i++]=g[k][i])m&&c[j]==i?k=!k,j++:0;}

Try it online!

Inputs:
g[0] is gene-sequence 1,
g[1] is gene-sequence 2,
c is cross-over points.
l is length of g[0] and g[1]
m is length of c
All array inputs are arrays of integers with 0-based index.

Outputs:
Output is stored in g[0]

macro a() in footer does pretty-printing of test-cases and result

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.