Skip to content

Commit

Permalink
Reversal Distance (updated)
Browse files Browse the repository at this point in the history
Implemented a greedy BFS algorithm based on reducing the number of break
points in a permutation.  This should yield the optimal reversal
distance for the vast majority of reversals, if not all reversals.
Closes #5.
  • Loading branch information
jschendel committed Jan 12, 2014
1 parent 3812421 commit f6405ac
Showing 1 changed file with 75 additions and 139 deletions.
214 changes: 75 additions & 139 deletions 042_REAR.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,146 +8,82 @@
URL: http://rosalind.info/problems/rear/
'''

from copy import deepcopy

with open('data/rosalind_rear.txt') as input_data:
Permutations = [pair.strip().split('\n') for pair in input_data.read().split('\n\n')]
for index, pair in enumerate(Permutations):
Permutations[index] = [map(int, perm.split()) for perm in pair]

# with open('D:/test.txt') as input_data:
# Permutations = [pair.strip().split('\n') for pair in input_data.read().split('\n\n')]
# for index, pair in enumerate(Permutations):
# Permutations[index] = [map(int, perm.split()) for perm in pair]

count_set =[]

count_list = []
A = deepcopy(Permutations)
for fixed_perm, perm in A:
i, count = 0, 0
while i < len(fixed_perm) - 1:
if fixed_perm[i] == perm[i]:
i += 1
else:
j = perm.index(fixed_perm[i])
perm[i:j+1] = reversed(perm[i:j+1])
count += 1

count_list.append(count)

print count_list
count_set.append(count_list)


count_list = []
A = deepcopy(Permutations)
for fixed_perm, perm in A:
i, count = len(perm)-1, 0
while i > 0:
if fixed_perm[i] == perm[i]:
i -= 1
else:
j = perm.index(fixed_perm[i])
perm[j:i+1] = reversed(perm[j:i+1])
count += 1

count_list.append(count)

print count_list
count_set.append(count_list)


A = deepcopy(Permutations)
count_list = []
for fixed_perm, perm in A:
count, left_index, right_index = 0, 0, len(perm)-1
while fixed_perm != perm:

while perm[left_index] == fixed_perm[left_index]:
left_index += 1
while perm[right_index] == fixed_perm[right_index]:
right_index -= 1

left_dist = perm.index(fixed_perm[left_index]) - left_index
right_dist = right_index - perm.index(fixed_perm[right_index])

if left_dist <= right_dist :
perm[left_index:left_index+left_dist+1] = reversed(perm[left_index:left_index+left_dist+1])
count += 1
else:
perm[right_index-right_dist:right_index+1] = reversed(perm[right_index-right_dist:right_index+1])
count += 1

count_list.append(count)

print count_list
count_set.append(count_list)


A = deepcopy(Permutations)
count_list = []
for fixed_perm, perm in A:
count, left_index, right_index = 0, 0, len(perm)-1
while fixed_perm != perm:

while perm[left_index] == fixed_perm[left_index]:
left_index += 1
while perm[right_index] == fixed_perm[right_index]:
right_index -= 1

left_dist = perm.index(fixed_perm[left_index]) - left_index
right_dist = right_index - perm.index(fixed_perm[right_index])

if left_dist >= right_dist :
perm[left_index:left_index+left_dist+1] = reversed(perm[left_index:left_index+left_dist+1])
count += 1
else:
perm[right_index-right_dist:right_index+1] = reversed(perm[right_index-right_dist:right_index+1])
count += 1

count_list.append(count)

print count_list
count_set.append(count_list)


A = deepcopy(Permutations)
count_list = []
for fixed_perm, perm in A:
count, left_index, right_index = 0, 0, len(perm)-1
while fixed_perm != perm:

while perm[left_index] == fixed_perm[left_index]:
left_index += 1
while perm[right_index] == fixed_perm[right_index]:
right_index -= 1

left_dist = perm.index(fixed_perm[left_index]) - left_index
right_dist = right_index - perm.index(fixed_perm[right_index])

if left_dist + right_dist > right_index - left_index + 1:
if left_dist >= right_dist :
perm[left_index:left_index+left_dist+1] = reversed(perm[left_index:left_index+left_dist+1])
count += 1
else:
perm[right_index-right_dist:right_index+1] = reversed(perm[right_index-right_dist:right_index+1])
count += 1
def breakpoint_count(permutation):
'''Returns the number of breakpoints in a given permutation.'''

elif left_dist <= right_dist :
perm[left_index:left_index+left_dist+1] = reversed(perm[left_index:left_index+left_dist+1])
count += 1
else:
perm[right_index-right_dist:right_index+1] = reversed(perm[right_index-right_dist:right_index+1])
count += 1

count_list.append(count)

print count_list
count_set.append(count_list)
# Prepend 0 and append len(permutation)+1 to check if the endpoints are in place.
permutation = [0] + list(permutation) + [len(permutation)+1]

min_count = [str(min(perm_count)) for perm_count in zip(*count_set)]
return sum(map(lambda x, y: abs(x-y) != 1, permutation[1:], permutation[:-1]))

print ' '.join(min_count)
with open('output/042_REAR.txt', 'w') as output_data:
output_data.write(' '.join(min_count))

def breakpoint_indices(permutation):
'''Returns the indices of all breakpoints in a given permutation.'''
from itertools import compress
# Prepend 0 and append len(permutation)+1 to check if the endpoints are in place.
permutation = [0] + list(permutation) + [len(permutation)+1]

return compress(xrange(len(permutation)-1), map(lambda x, y: abs(x-y) != 1, permutation[1:], permutation[:-1]))


def greedy_breakpoint_bfs(perm1, perm2):
'''Performs a greedy breakpoint reduction based BFS sorting of a given permutation.'''
from itertools import product

# Normalize the permutations so that we sort to the identity permutation (1,2,3, ..., n).
to_identity = {value: i + 1 for i, value in enumerate(perm2)}
normalized_perm = [to_identity[value] for value in perm1]

# Quick lambda function to reverse a region in the permutation.
rev_perm = lambda perm, i, j: perm[:i] + perm[i:j + 1][::-1] + perm[j + 1:]

# Initialize Variables
normalized_perm = tuple(normalized_perm)
current_perms = [normalized_perm]
min_breaks = breakpoint_count(normalized_perm)
dist = 0

# Run the greedy BFS breakpoint reduction sorting.
while True:
new_perms = []
dist += 1
# Iterate over all combinations of breakpoint indices for all current minimal permutations.
for perm in current_perms:
for rev_ind in product(breakpoint_indices(perm), repeat=2):
# Store some temporary variables for the given iteration.
temp_perm = tuple(rev_perm(perm, rev_ind[0], rev_ind[1] - 1))
temp_breaks = breakpoint_count(temp_perm)

# Done we have no breakpoints.
if temp_breaks == 0:
return dist

# Create a new dictionary and update the minimum number of breakpoints if we've found a reduction.
elif temp_breaks < min_breaks:
min_breaks = temp_breaks
new_perms = [temp_perm]

# Add to the dictionary if the current breakpoints match the minimum number.
elif temp_breaks == min_breaks:
new_perms.append(temp_perm)

current_perms = new_perms


if __name__ == '__main__':

# Read the input data.
with open('data/rosalind_rear.txt') as input_data:
permutations_list = [pair.strip().split('\n') for pair in input_data.read().split('\n\n')]
for index, pair in enumerate(permutations_list):
permutations_list[index] = [map(int, perm.split()) for perm in pair]

# Get the reversal distances.
# Apply both permutation orders since, although unlikely, sometimes one order may not be optimal.
min_dists = [str(min(greedy_breakpoint_bfs(p1, p2), greedy_breakpoint_bfs(p2, p1))) for p1, p2 in permutations_list]

# Print and save the answer.
print ' '.join(min_dists)
with open('output/042_REAR.txt', 'w') as output_data:
output_data.write(' '.join(min_dists))

0 comments on commit f6405ac

Please sign in to comment.