Angella Andrea - Italian Blog

Infinita passione per lo sviluppo software !
posts - 129, comments - 318, trackbacks - 7

My Links

News

MIT OpenCourseWare: I'm invested Wikipedia Affiliate Button


Sto leggendo:

Archives

Post Categories

Siti web realizzati

Siti web tecnici

Algoritmi – Determinare dal testo di una rivista se e’ possibile creare una ransom note


Problema 1:

Fino ad ora non sapevo cosa fosse una ransom note.
Praticamente si tratta di un testo realizzato utilizzando ritagli da una rivista.

ransom note

Dato il testo di una ransom note e il testo della rivista, dire se e’ possibile realizzare la ransom note.

Problema 2:

Risolvere lo stesso problema ma ritagliando parole invece che singoli caratteri.

Input:

Testo della ransom note che si vuole creare (stringa).
Testo della rivista da cui si vuole ritagliare (stringa).

Output:

True se e’ possibile realizzare la ransom note, False altrimenti.

Il mio ragionamento:

Scansiono due volte i testi e costruisco due vettori che conteggiano il numero di occorrenze di ciasun carattere. Fatto questo controllo che ci sia sempre un numero sufficiente di lettere per costruire la ransom note confrontando uno ad uno gli elementi corrispondenti di questi due array.

Per quanto riguarda il problema 2 invece di utilizzare due vettori, utilizzo due tabelle hash. La logica e’ sostanzialmente la stessa.

Le mie soluzioni:

public static bool IsCharacterRansomNotePossible(string noteText, string magazineText)
{
    if (String.IsNullOrEmpty(noteText) || String.IsNullOrEmpty(magazineText))
        throw new ArgumentException("noteText and/or magazineText cannot be null or empty");

    const int N = Char.MaxValue + 1;
    
    uint[] noteCount = new uint[N];
    uint[] magazineCount = new uint[N];

    foreach (char c in noteText) ++noteCount[c];            
    foreach (char c in magazineText) ++magazineCount[c];

    for (int i = 0; i < N; ++i)
        if (noteCount[i] > magazineCount[i])
            if(!char.IsWhiteSpace((char)i))
                return false;

    return true;
}

public static bool IsWordRansomNotePossible(string noteText, string magazineText)
{
    if (String.IsNullOrEmpty(noteText) || String.IsNullOrEmpty(magazineText))
        throw new ArgumentException("noteText and/or magazineText cannot be null or empty");

    var noteCount = new Dictionary<string, int>();
    var magazineCount = new Dictionary<string, int>();

    var noteWords = noteText.Split();
    var magazineWords = magazineText.Split();

    foreach (string s in noteWords)
        if (noteCount.ContainsKey(s)) ++noteCount[s];
        else noteCount[s] = 1;

    foreach (string s in magazineWords)
        if (magazineCount.ContainsKey(s)) ++magazineCount[s];
        else magazineCount[s] = 1;

    foreach (string word in noteCount.Keys)
        if (!magazineCount.ContainsKey(word) || noteCount[word] > magazineCount[word])
            return false;

    return true;
}


Qualunque commento e’ ben accetto. Il codice non e’ stato testato rigorosamente.

Soluzione di Ludovico Van:

public static bool IsCharRansomNotePossible(string note, string source)
{
    note = note == null ? string.Empty : note.Trim();
    source = source == null ? string.Empty : source.Trim();

    if (note.Length == 0)
        return true;

    int[] charCounts = new int[1 + char.MaxValue];

    int charSetCount = 0;

    foreach (char ch in note)
        if (!char.IsWhiteSpace(ch))
            if (charCounts[ch]++ == 0)
                ++charSetCount;

    foreach (char ch in source)
        if (charCounts[ch] != 0)
            if (--charCounts[ch] == 0)
                if (--charSetCount == 0)
                    return true;

    return false;
}

Print | posted on giovedì 2 settembre 2010 0.51 | Filed Under [ Algoritmica ]

Feedback

Gravatar

# re: Algoritmi – Determinare dal testo di una rivista se e’ possibile creare una ransom note

Penso che l'utilizzo del metodo Split() nel secondo problema porti ad un eccessivo utilizzo della memoria visto che non e' necessario avere contemporaneamente in memoria tutte le parole. Penso sarebbe meglio sfruttare la lazy evaluation per risparmiare memoria considerato che il testo di una rivista puo' contenere tantissime parole.
02/09/2010 0.55 | Andrea Angella
Gravatar

# re: Algoritmi – Determinare dal testo di una rivista se e’ possibile creare una ransom note

Personalmente avrei usato il metodo "seek & destroy", ovvero le funzioni string.IndexOf e string.Remove per trovare ogni carattero o parola del parametro noteText in magazineText.
Una finezza potrebbe essere la verifica della lunghezza delle due stringa in testa alle funzioni: se magazineText è più corta di noteText non vale la pena continuare.
02/09/2010 11.58 | Marco
Gravatar

# re: Algoritmi – Determinare dal testo di una rivista se e’ possibile creare una ransom note

Mi sono divertito un po' con la IsCharacterRansomNotePossible.

Ho provato 4 versioni: di seguito un tentativo di benchmark dove la versione 3 corrisponde al il tuo codice. Pare di capire che la versione 0 con il dictionary e' nettamente meglio della versione 2 con l'array fino a circa 2000 caratteri di lunghezza della nota (o in quell'ordine), poi il rapporto si inverte. Sono allocazione e conseguente azzeramento dell'area di memoria ad incidere di piu' sui tempi.

(Btw, esiste un modo per formattare il codice in questi post?)

-LV

[0] len = 54 / 449, iter = 500, elapsed = 16 ms
[1] len = 54 / 449, iter = 500, elapsed = 244 ms
[2] len = 54 / 449, iter = 500, elapsed = 223 ms
[3] len = 54 / 449, iter = 500, elapsed = 717 ms

[0] len = 1998 / 16613, iter = 200, elapsed = 169 ms
[1] len = 1998 / 16613, iter = 200, elapsed = 173 ms
[2] len = 1998 / 16613, iter = 200, elapsed = 156 ms
[3] len = 1998 / 16613, iter = 200, elapsed = 351 ms

[0] len = 5994 / 49839, iter = 100, elapsed = 257 ms
[1] len = 5994 / 49839, iter = 100, elapsed = 177 ms
[2] len = 5994 / 49839, iter = 100, elapsed = 139 ms
[3] len = 5994 / 49839, iter = 100, elapsed = 244 ms

----------------------------------------------------------------------
public static bool IsCharacterRansomNotePossible_0(string noteText, string magazineText)
{
//System.Diagnostics.Debug.Assert(char.MinValue == (char)0, "FAILED: char.MinValue == 0.");

if (string.IsNullOrWhiteSpace(noteText))
return true;

if (string.IsNullOrWhiteSpace(magazineText))
return false;

noteText = noteText.Trim();
magazineText = magazineText.Trim();

Dictionary<char, int> charOccurrences = new Dictionary<char, int>(Math.Min(noteText.Length, 80));

foreach (char ch in noteText)
{
if (!char.IsWhiteSpace(ch))
if (charOccurrences.ContainsKey(ch))
++charOccurrences[ch];
else
charOccurrences[ch] = 1;
}

if (charOccurrences.Count == 0)
return true;

foreach (char ch in magazineText)
{
if (charOccurrences.ContainsKey(ch))
if (--charOccurrences[ch] == 0)
{
charOccurrences.Remove(ch);

if (charOccurrences.Count == 0)
return true;
}
}

//System.Diagnostics.Debug.Assert(charOccurrences.Count != 0, "DEBUG: charOccurrences is not empty.");

return false;
}
----------------------------------------------------------------------

----------------------------------------------------------------------
/// <remarks>Discounts white space.</remarks>
public static bool IsCharacterRansomNotePossible_2(string noteText, string magazineText)
{
//System.Diagnostics.Debug.Assert(char.MinValue == (char)0, "FAILED: char.MinValue == 0.");

if (string.IsNullOrWhiteSpace(noteText))
return true;

if (string.IsNullOrWhiteSpace(magazineText))
return false;

noteText = noteText.Trim();
magazineText = magazineText.Trim();

int setCount = 0;

int[] charOccurrences = new int[1 + char.MaxValue];

foreach (char ch in noteText)
if (!char.IsWhiteSpace(ch))
{
if (charOccurrences[ch] == 0)
++setCount;
++charOccurrences[ch];
}

if (setCount == 0)
return true;

foreach (char ch in magazineText)
{
if (charOccurrences[ch] != 0)
{
--charOccurrences[ch];
if (charOccurrences[ch] == 0)
--setCount;
if (setCount == 0)
return true;
}
}

return false;
}
----------------------------------------------------------------------
03/09/2010 4.10 | LudovicoVan
Gravatar

# re: Algoritmi – Determinare dal testo di una rivista se e’ possibile creare una ransom note

Nel codice che ho incollato sopra mancano un paio di graffe... comunque il risultato cambia di poco.

Questo e' un benchmark fino a 100,000 caratteri:

[0] len = 54 / 449, iter = 1000, elapsed = 28 ms
[1] len = 54 / 449, iter = 1000, elapsed = 305 ms
[2] len = 54 / 449, iter = 1000, elapsed = 294 ms
[3] len = 54 / 449, iter = 1000, elapsed = 1070 ms

[0] len = 1998 / 16613, iter = 400, elapsed = 340 ms
[1] len = 1998 / 16613, iter = 400, elapsed = 278 ms
[2] len = 1998 / 16613, iter = 400, elapsed = 254 ms
[3] len = 1998 / 16613, iter = 400, elapsed = 584 ms

[0] len = 101898 / 847263, iter = 500, elapsed = 22075 ms
[1] len = 101898 / 847263, iter = 500, elapsed = 10400 ms
[2] len = 101898 / 847263, iter = 500, elapsed = 8296 ms
[3] len = 101898 / 847263, iter = 500, elapsed = 9140 ms

E adesso davvero buonanotte... :)

-LV
03/09/2010 4.55 | LudovicoVan
Gravatar

# re: Algoritmi – Determinare dal testo di una rivista se e’ possibile creare una ransom note

Qui la migliore e' la numero 4, che e' la 2 facendo l'allocazione prima. La tua con allocazione prima e' la 5.

[0] len = 54 / 449, iter = 1000, elapsed = 30 ms
[1] len = 54 / 449, iter = 1000, elapsed = 376 ms
[2] len = 54 / 449, iter = 1000, elapsed = 367 ms
[3] len = 54 / 449, iter = 1000, elapsed = 1384 ms
[4] len = 54 / 449, iter = 1000, elapsed = 5 ms
[5] len = 54 / 449, iter = 1000, elapsed = 514 ms

[0] len = 1998 / 16613, iter = 400, elapsed = 340 ms
[1] len = 1998 / 16613, iter = 400, elapsed = 312 ms
[2] len = 1998 / 16613, iter = 400, elapsed = 275 ms
[3] len = 1998 / 16613, iter = 400, elapsed = 698 ms
[4] len = 1998 / 16613, iter = 400, elapsed = 126 ms
[5] len = 1998 / 16613, iter = 400, elapsed = 324 ms

[0] len = 41958 / 348873, iter = 500, elapsed = 9129 ms
[1] len = 41958 / 348873, iter = 500, elapsed = 4486 ms
[2] len = 41958 / 348873, iter = 500, elapsed = 3631 ms
[3] len = 41958 / 348873, iter = 500, elapsed = 4364 ms
[4] len = 41958 / 348873, iter = 500, elapsed = 3408 ms
[5] len = 41958 / 348873, iter = 500, elapsed = 3847 ms

-LV
03/09/2010 5.20 | LudovicoVan
Gravatar

# re: Algoritmi – Determinare dal testo di una rivista se e’ possibile creare una ransom note

Ciao Ludovico,
non credo sia possibile formattare il codice nei commenti. Tuttavia posso aggiungere le tue soluzioni formattate al post.

Quando ho un po' di tempo le analizzero'. Il problema e' verificarne la correttezza prima di fare qualsiasi benchmark.

Per quanto riguarda la mia soluzione penso che dovresti mettere noteCount e magazineCount fuori dalla funzione. In quel caso penso proprio che la performance diventi notevole visto che non devi riallocare i vettori ad ogni iterazione. Tuttavia vanno azzerati, bho non so dovrei provare.

uint[] noteCount = new uint[N];
uint[] magazineCount = new uint[N];
03/09/2010 14.39 | Andrea Angella
Gravatar

# re: Algoritmi – Determinare dal testo di una rivista se e’ possibile creare una ransom note

L'altra notte me ne sono andato un po' per la tangente, ottimizzare senza un problema reale con requisiti precisi non ha molto senso, per cui il discorso dell'allocazione contro azzeramento lo lascerei cadere.

Tornando al problema come lo avevi posto inizialmente, ti riallego la mia migliore versione un po' ripulita:

================================================

/// <remarks>Discounts white space.</remarks>
public static bool IsCharRansomNotePossible(string note, string source)
{
note = note == null ? string.Empty : note.Trim();
source = source == null ? string.Empty : source.Trim();

int[] charCounts = new int[1 + char.MaxValue];

int charSetCount = 0;

foreach (char ch in note)
if (!char.IsWhiteSpace(ch))
if (charCounts[ch]++ == 0)
++charSetCount;

foreach (char ch in source)
if (charCounts[ch] != 0)
if (--charCounts[ch] == 0)
if (--charSetCount == 0)
return true;

return false;
}

================================================

-LV
04/09/2010 5.25 | LudovicoVan
Gravatar

# re: Algoritmi – Determinare dal testo di una rivista se e’ possibile creare una ransom note

Scusa, l'ora e' tarda e io mi perdo i pezzi...

================================================

/// <remarks>Discounts white space.</remarks>
public static bool IsCharRansomNotePossible(string note, string source)
{
note = note == null ? string.Empty : note.Trim();
source = source == null ? string.Empty : source.Trim();

if (note.Length == 0)
return true;

int[] charCounts = new int[1 + char.MaxValue];

int charSetCount = 0;

foreach (char ch in note)
if (!char.IsWhiteSpace(ch))
if (charCounts[ch]++ == 0)
++charSetCount;

foreach (char ch in source)
if (charCounts[ch] != 0)
if (--charCounts[ch] == 0)
if (--charSetCount == 0)
return true;

return false;
}

================================================

-LV
04/09/2010 5.45 | LudovicoVan
Gravatar

# re: Algoritmi – Determinare dal testo di una rivista se e’ possibile creare una ransom note

Ho pubblicato la tua versione formattata. Thanks.

Effettivamente i whitespace devono essere ignorati, quello era un bug nel mio codice.

Mi piace l'idea del charSetCount per velocizzare la verifica finale.
04/09/2010 13.22 | Andrea Angella
Comments have been closed on this topic.

Powered by: