← Catalogo
SORT
PyJS

Sorting by Reversals

CombinatoricsGenome Rearrangements

Pagina originale su rosalind.info

Descrizione

A differenza di "Reversal Distance" (REAR), che chiede solo il numero minimo di inversioni, questo problema chiede di fornire anche l'effettiva sequenza minima di inversioni che trasforma π in γ. Una inversione è codificata dai due indici (1-based) degli estremi dell'intervallo invertito.

Given

Due permutazioni π e γ, ciascuna di lunghezza 10.

Return

La reversal distance d_rev(π, γ), seguita da una collezione di inversioni che ordina π in γ. Se esistono più collezioni possibili, se ne può restituire una qualsiasi.

Sample Dataset

1 2 3 4 5 6 7 8 9 10
1 8 9 3 2 7 6 5 4 10

Sample Output

2
4 9
2 5

Nota implementativa

Lo script usa una BFS bidirezionale (una ricerca in ampiezza che parte contemporaneamente da π verso γ e da γ verso π, fermandosi quando le due esplorazioni si incontrano), molto più veloce di una BFS unidirezionale per permutazioni di lunghezza 10, dove la distanza di reversal può arrivare fino a 9.

La mia esecuzione

08/24/2026 11:24:47

Input · dataset.txt

9 4 7 5 2 3 10 6 1 8
2 7 8 5 4 10 9 1 3 6

Output · run_log.txt

OK
8
2 6
3 9
4 10
6 7
2 9
1 7
1 3
1 2

Esegui nel browser · Pyodide

Mostra il codice sorgente (problem.py)
#http://rosalind.info/problems/sort/

def lettura(filename):
    with open(filename) as f:
        righe = [r.split() for r in f if r.strip() != ""]
    pi = tuple(int(x) for x in righe[0])
    gamma = tuple(int(x) for x in righe[1])
    return pi, gamma

def inverti(perm, i, j):
    return perm[:i] + perm[i:j + 1][::-1] + perm[j + 1:]

def bfs_bidirezionale(inizio, fine):
    n = len(inizio)
    mosse = [(i, j) for i in range(n) for j in range(i + 1, n)]

    genitore_avanti = {inizio: None}
    mossa_avanti = {}
    fronte_avanti = [inizio]

    genitore_indietro = {fine: None}
    mossa_indietro = {}
    fronte_indietro = [fine]

    if inizio == fine:
        return []

    incontro = None
    while incontro is None:
        if len(fronte_avanti) <= len(fronte_indietro):
            nuovo_fronte = []
            for perm in fronte_avanti:
                for (i, j) in mosse:
                    nuovo = inverti(perm, i, j)
                    if nuovo not in genitore_avanti:
                        genitore_avanti[nuovo] = perm
                        mossa_avanti[nuovo] = (i, j)
                        if nuovo in genitore_indietro:
                            incontro = nuovo
                            break
                        nuovo_fronte.append(nuovo)
                if incontro is not None:
                    break
            fronte_avanti = nuovo_fronte
        else:
            nuovo_fronte = []
            for perm in fronte_indietro:
                for (i, j) in mosse:
                    nuovo = inverti(perm, i, j)
                    if nuovo not in genitore_indietro:
                        genitore_indietro[nuovo] = perm
                        mossa_indietro[nuovo] = (i, j)
                        if nuovo in genitore_avanti:
                            incontro = nuovo
                            break
                        nuovo_fronte.append(nuovo)
                if incontro is not None:
                    break
            fronte_indietro = nuovo_fronte

    # ricostruisci il percorso: inizio -> incontro
    percorso_avanti = []
    corrente = incontro
    while genitore_avanti[corrente] is not None:
        percorso_avanti.append(mossa_avanti[corrente])
        corrente = genitore_avanti[corrente]
    percorso_avanti.reverse()

    # ricostruisci il percorso: incontro -> fine
    percorso_indietro = []
    corrente = incontro
    while genitore_indietro[corrente] is not None:
        percorso_indietro.append(mossa_indietro[corrente])
        corrente = genitore_indietro[corrente]

    return percorso_avanti + percorso_indietro

def main():
    pi, gamma = lettura("dataset.txt")
    mosse = bfs_bidirezionale(pi, gamma)
    print(len(mosse))
    for i, j in mosse:
        print(i + 1, j + 1)

if __name__ == "__main__":
    # execute only if run as a script
    main()

Soluzione JavaScript

Esegui ora, live

Mostra il codice sorgente
// Sorting by Reversals (Rosalind ID: SORT) - soluzione JavaScript
// indipendente, non una trascrizione di problem.py: stesso algoritmo
// (BFS bidirezionale sul grafo delle permutazioni generato dai
// reversal, che sfrutta il fatto che un reversal è la propria
// inversa per ricostruire il percorso concatenando le due metà),
// riscritta in modo idiomatico per JS.
//
// Le permutazioni sono tenute come array di interi per le operazioni,
// e come stringa "a,b,c,..." per le chiavi delle Map (equivalente delle
// tuple Python, che sono hashable direttamente).
//
// Contratto: riceve il contenuto testuale di dataset.txt, restituisce
// l'output testuale (stessa forma dell'output Python).
function inverti(perm, i, j) {
  const nuovo = perm.slice();
  for (let a = i, b = j; a < b; a++, b--) {
    const tmp = nuovo[a];
    nuovo[a] = nuovo[b];
    nuovo[b] = tmp;
  }
  return nuovo;
}

function chiave(perm) {
  return perm.join(",");
}

function bfsBidirezionale(inizio, fine) {
  const n = inizio.length;
  const mosse = [];
  for (let i = 0; i < n; i++) {
    for (let j = i + 1; j < n; j++) mosse.push([i, j]);
  }

  const chiaveInizio = chiave(inizio);
  const chiaveFine = chiave(fine);

  if (chiaveInizio === chiaveFine) return [];

  // genitore_*: chiave permutazione -> chiave permutazione genitore (o
  // null per la radice). mossa_*: chiave permutazione -> [i, j] usata
  // per raggiungerla dal genitore.
  const genitoreAvanti = new Map([[chiaveInizio, null]]);
  const mossaAvanti = new Map();
  let fronteAvanti = [inizio];

  const genitoreIndietro = new Map([[chiaveFine, null]]);
  const mossaIndietro = new Map();
  let fronteIndietro = [fine];

  let incontro = null;

  while (incontro === null) {
    if (fronteAvanti.length <= fronteIndietro.length) {
      const nuovoFronte = [];
      outer: for (const perm of fronteAvanti) {
        const kPerm = chiave(perm);
        for (const [i, j] of mosse) {
          const nuovo = inverti(perm, i, j);
          const kNuovo = chiave(nuovo);
          if (!genitoreAvanti.has(kNuovo)) {
            genitoreAvanti.set(kNuovo, kPerm);
            mossaAvanti.set(kNuovo, [i, j]);
            if (genitoreIndietro.has(kNuovo)) {
              incontro = kNuovo;
              break outer;
            }
            nuovoFronte.push(nuovo);
          }
        }
      }
      fronteAvanti = nuovoFronte;
    } else {
      const nuovoFronte = [];
      outer: for (const perm of fronteIndietro) {
        const kPerm = chiave(perm);
        for (const [i, j] of mosse) {
          const nuovo = inverti(perm, i, j);
          const kNuovo = chiave(nuovo);
          if (!genitoreIndietro.has(kNuovo)) {
            genitoreIndietro.set(kNuovo, kPerm);
            mossaIndietro.set(kNuovo, [i, j]);
            if (genitoreAvanti.has(kNuovo)) {
              incontro = kNuovo;
              break outer;
            }
            nuovoFronte.push(nuovo);
          }
        }
      }
      fronteIndietro = nuovoFronte;
    }
  }

  // Ricostruisce il percorso: inizio -> incontro.
  const percorsoAvanti = [];
  let corrente = incontro;
  while (genitoreAvanti.get(corrente) !== null) {
    percorsoAvanti.push(mossaAvanti.get(corrente));
    corrente = genitoreAvanti.get(corrente);
  }
  percorsoAvanti.reverse();

  // Ricostruisce il percorso: incontro -> fine (il reversal è la
  // propria inversa, quindi si può riusare la stessa mossa).
  const percorsoIndietro = [];
  corrente = incontro;
  while (genitoreIndietro.get(corrente) !== null) {
    percorsoIndietro.push(mossaIndietro.get(corrente));
    corrente = genitoreIndietro.get(corrente);
  }

  return [...percorsoAvanti, ...percorsoIndietro];
}

function lettura(datasetText) {
  const righe = datasetText
    .split("\n")
    .map((r) => r.trim())
    .filter((r) => r !== "")
    .map((r) => r.split(/\s+/).map(Number));

  return { pi: righe[0], gamma: righe[1] };
}

export default function solve(datasetText) {
  const { pi, gamma } = lettura(datasetText);

  if (!pi || !gamma || pi.length !== gamma.length) {
    throw new Error("Input non valido: attese due permutazioni della stessa lunghezza");
  }

  const mosse = bfsBidirezionale(pi, gamma);

  const righeOutput = [String(mosse.length)];
  for (const [i, j] of mosse) {
    righeOutput.push(`${i + 1} ${j + 1}`);
  }

  return `${righeOutput.join("\n")}\n`;
}