Crea grafici narrativi in ​​stile xkcd


45

In una delle più iconiche strisce xkcd, Randall Munroe ha visualizzato le linee temporali di diversi film nelle classifiche narrative:

inserisci qui la descrizione dell'immagine (Fare clic per una versione più grande.)

Fonte: xkcd n . 657 .

Data una specifica della linea temporale di un film (o di qualche altra narrativa), devi generare tale grafico. Questo è un concorso di popolarità, quindi vincerà la risposta con il maggior numero di voti (netti).

Requisiti minimi

Per restringere un po 'le specifiche, ecco il set minimo di funzionalità che ogni risposta deve implementare:

  • Prendi come input un elenco di nomi di personaggi, seguito da un elenco di eventi. Ogni evento è un elenco di personaggi morenti o un elenco di gruppi di personaggi (che indica quali personaggi sono attualmente insieme). Ecco un esempio di come potrebbe essere codificata la narrativa di Jurassic Park:

    ["T-Rex", "Raptor", "Raptor", "Raptor", "Malcolm", "Grant", "Sattler", "Gennaro",
     "Hammond", "Kids", "Muldoon", "Arnold", "Nedry", "Dilophosaurus"]
    [
      [[0],[1,2,3],[4],[5,6],[7,8,10,11,12],[9],[13]],
      [[0],[1,2,3],[4,7,5,6,8,9,10,11,12],[13]],
      [[0],[1,2,3],[4,7,5,6,8,9,10],[11,12],[13]],
      [[0],[1,2,3],[4,7,5,6,9],[8,10,11,12],[13]],
      [[0,4,7],[1,2,3],[5,9],[6,8,10,11],[12],[13]],
      [7],
      [[5,9],[0],[4,6,10],[1,2,3],[8,11],[12,13]],
      [12],
      [[0, 5, 9], [1, 2, 3], [4, 6, 10, 8, 11], [13]], 
      [[0], [5, 9], [1, 2], [3, 11], [4, 6, 10, 8], [13]], 
      [11], 
      [[0], [5, 9], [1, 2, 10], [3, 6], [4, 8], [13]], 
      [10], 
      [[0], [1, 2, 9], [5, 6], [3], [4, 8], [13]], 
      [[0], [1], [9, 5, 6], [3], [4, 8], [2], [13]], 
      [[0, 1, 9, 5, 6, 3], [4, 8], [2], [13]], 
      [1, 3], 
      [[0], [9, 5, 6, 3, 4, 8], [2], [13]]
    ]
    

    Ad esempio, la prima riga significa che all'inizio del grafico, T-Rex è un solitario, i tre Rapaci sono insieme, Malcolm è solo, Grant e Sattler sono insieme, ecc. Il penultimo evento significa che due dei Rapaci muoiono .

    Quanto esattamente ti aspetti che l'input dipenda da te, purché sia ​​possibile specificare questo tipo di informazioni. Ad esempio è possibile utilizzare qualsiasi formato elenco conveniente. Puoi anche aspettarti che i personaggi degli eventi siano di nuovo i nomi completi dei personaggi, ecc.

    Puoi (ma non devi) presumere che ogni elenco di gruppi contenga ciascun personaggio vivente in esattamente un gruppo. Tuttavia, si dovrebbe non dare per scontato che i gruppi o caratteri all'interno di un evento sono in ordine particolarmente conveniente.

  • Renderizza su schermo o file (come grafica vettoriale o raster) un grafico che ha una linea per ogni personaggio. Ogni riga deve essere etichettata con un nome di carattere all'inizio della riga.

  • Per ogni evento normale, ci deve essere, in ordine, una sezione del diagramma in cui i gruppi di personaggi sono chiaramente assomigliati dalla vicinanza delle rispettive linee.
  • Per ogni evento di morte, le linee dei personaggi rilevanti devono terminare in un blob visibile.
  • Non , non è necessario riprodurre tutte le altre caratteristiche di trame di Randall, né si deve riprodurre il suo stile di disegno. Linee dritte con curve strette, tutte in nero, senza ulteriori etichette e un titolo è perfettamente adatto per partecipare alla competizione. Inoltre, non è necessario utilizzare lo spazio in modo efficiente, ad esempio potresti potenzialmente semplificare il tuo algoritmo spostando le linee verso il basso solo per incontrare altri personaggi, purché vi sia una direzione temporale percepibile.

Ho aggiunto una soluzione di riferimento che soddisfa esattamente questi requisiti minimi.

Rendendolo carino

Questo è un concorso di popolarità, quindi, oltre a ciò, puoi implementare qualsiasi fantasia tu voglia. L'aggiunta più importante è un algoritmo di layout decente che rende il grafico più leggibile, ad esempio che rende facili da seguire le curve nelle linee e che riduce il numero di incroci di linea necessari. Questo è il problema algoritmico principale di questa sfida! I voti decideranno in che misura il tuo algoritmo si comporta nel mantenere in ordine il grafico.

Ma qui ci sono altre idee, la maggior parte basate sulle carte di Randall:

decorazioni:

  • Linee colorate.
  • Un titolo per la trama.
  • La linea di etichettatura termina.
  • Rietichettatura automatica delle linee che hanno attraversato una sezione occupata.
  • Stile disegnato a mano (o altro? Come ho detto, non è necessario riprodurre lo stile di Randall se hai un'idea migliore) per linee e caratteri.
  • Orientamento personalizzabile dell'asse temporale.

Espressività aggiuntiva:

  • Eventi / gruppi / morti nominati.
  • Linee che scompaiono e riappaiono.
  • I personaggi entrano in ritardo.
  • Punti salienti che indicano le proprietà (trasferibili?) Dei caratteri (ad esempio, vedere il portatore di anelli nella tabella LotR).
  • Codifica di informazioni aggiuntive nell'asse di raggruppamento (ad es. Informazioni geografiche come nel diagramma LotR).
  • Viaggio nel tempo?
  • Realtà alternative?
  • Un personaggio si trasforma in un altro?
  • Due personaggi si fondono? (Una divisione del personaggio?)
  • 3D? (Se vai davvero così lontano, assicurati di utilizzare effettivamente la dimensione aggiuntiva per visualizzare qualcosa!)
  • Qualsiasi altra caratteristica pertinente, che potrebbe essere utile per visualizzare la narrazione di un film (o libro, ecc.).

Naturalmente, molti di questi richiedono input aggiuntivi e sei libero di aumentare il formato di input secondo necessità, ma ti preghiamo di documentare come è possibile inserire i dati.

Includi uno o due esempi per mostrare le funzionalità implementate.

La tua soluzione dovrebbe essere in grado di gestire qualsiasi input valido, ma va assolutamente bene se si adatta meglio a determinati tipi di narrazioni rispetto ad altri.

Criteri di voto

Non ho illusioni di poter dire alla gente come dovrebbero spendere i loro voti, ma ecco alcune linee guida suggerite in ordine di importanza:

  • Downvote le risposte che sfruttano le lacune, quelle standard o altre, o codificano uno o più risultati.
  • Non votare le risposte che non soddisfano i requisiti minimi (non importa quanto il resto possa essere).
  • Innanzitutto, elabora algoritmi di layout gradevoli. Ciò include le risposte che non utilizzano molto spazio verticale riducendo al minimo l'attraversamento delle linee per mantenere leggibile il grafico o che riescono a codificare informazioni aggiuntive nell'asse verticale. Visualizzare i raggruppamenti senza fare un gran casino dovrebbe essere l'obiettivo principale di questa sfida, in modo che questo rimanga un concorso di programmazione con un interessante problema algoritmico a cuore.
  • Valorizza le funzionalità opzionali che aggiungono potenza espressiva (cioè non sono solo pura decorazione).
  • Infine, valuti la bella presentazione.

7
perché code-golf non ha abbastanza xkcd
orgoglioso haskeller l'

8
@proudhaskeller PPCG non può mai avere abbastanza xkcd. ;) Ma non credo che abbiamo ancora tentato di mettere alla prova le sue informazioni / grafica di grandi dimensioni, quindi spero di portare qualcosa di nuovo sul tavolo con questo. E sono sicuro che alcuni degli altri farebbero anche sfide molto diverse e interessanti.
Martin Ender,

Va bene se la mia soluzione gestisce solo 12 uomini arrabbiati, Duello (Spielberg, 1971, automobilista normale vs camionista impazzito) e Aerei, treni e automobili? ;-)
Level River St

4
mi chiedo come sarebbe l'input per il primer ...
Joshua,

1
@ping Sì, questa era l'idea. Se un evento contiene ulteriori elenchi, è un raggruppamento di elenchi. quindi [[x,y,z]]significherebbe che tutti i personaggi sono attualmente insieme. Ma se l'evento non contiene elenchi, ma solo personaggi direttamente, è addirittura una morte, quindi nella stessa situazione [x,y,z]significa che quei tre personaggi muoiono. Sentiti libero di usare un altro formato, con un'indicazione esplicita se qualcosa è un evento di morte o di raggruppamento se ciò ti aiuta. Il formato sopra è solo un suggerimento. Finché il tuo formato di input è almeno altrettanto espressivo, puoi usare qualcos'altro.
Martin Ender,

Risposte:


18

Python3 con numpy, scipy e matplotlib

Jurassic Park

modifica :

  • Ho cercato di mantenere i gruppi nella stessa posizione relativa tra gli eventi, quindi la sorted_eventfunzione.
  • Nuova funzione per calcolare la posizione y dei caratteri ( coords).
  • Ogni evento vivo viene tracciato due volte ora, quindi i personaggi si uniscono meglio.
  • Aggiunta la legenda e l'etichetta degli assi rimossi.
import math
import numpy as np
from scipy.interpolate import interp1d
from matplotlib import cm, pyplot as plt


def sorted_event(prev, event):
    """ Returns a new sorted event, where the order of the groups is
    similar to the order in the previous event. """
    similarity = lambda a, b: len(set(a) & set(b)) - len(set(a) ^ set(b))
    most_similar = lambda g: max(prev, key=lambda pg: similarity(g, pg))
    return sorted(event, key=lambda g: prev.index(most_similar(g)))


def parse_data(chars, events):
    """ Turns the input data into 3 "tables":
    - characters: {character_id: character_name}
    - timelines: {character_id: [y0, y1, y2, ...],
    - deaths: {character_id: (x, y)}
    where x and y are the coordinates of a point in the xkcd like plot.
    """
    characters = dict(enumerate(chars))
    deaths = {}
    timelines = {char: [] for char in characters}

    def coords(character, event):
        for gi, group in enumerate(event):
            if character in group:
                ci = group.index(character)
                return (gi + 0.5 * ci / len(group)) / len(event)
        return None

    t = 0
    previous = events[0]
    for event in events:
        if isinstance(event[0], list):
            previous = event = sorted_event(previous, event)
            for character in [c for c in characters if c not in deaths]:
                timelines[character] += [coords(character, event)] * 2
            t += 2
        else:
            for char in set(event) - set(deaths):
                deaths[char] = (t-1, timelines[char][-1])

    return characters, timelines, deaths


def plot_data(chars, timelines, deaths):
    """ Draws a nice xkcd like movie timeline """

    plt.xkcd()  # because python :)

    fig = plt.figure(figsize=(16,8))
    ax = fig.add_subplot(111)
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    ax.set_xlim([0, max(map(len, timelines.values()))])

    color_floats = np.linspace(0, 1, len(chars))
    color_of = lambda char_id: cm.Accent(color_floats[char_id])

    for char_id in sorted(chars):
        y = timelines[char_id]
        f = interp1d(np.linspace(0, len(y)-1, len(y)), y, kind=5)
        x = np.linspace(0, len(y)-1, len(y)*10)
        ax.plot(x, f(x), c=color_of(char_id))

    x, y = zip(*(deaths[char_id] for char_id in sorted(deaths)))
    ax.scatter(x, y, c=np.array(list(map(color_of, sorted(deaths)))), 
               zorder=99, s=40)

    ax.legend(list(map(chars.get, sorted(chars))), loc='best', ncol=4)
    fig.savefig('testplot.png')


if __name__ == '__main__':
    chars = [
        "T-Rex","Raptor","Raptor","Raptor","Malcolm","Grant","Sattler",
        "Gennaro","Hammond","Kids","Muldoon","Arnold","Nedry","Dilophosaurus"
    ]
    events = [
        [[0],[1,2,3],[4],[5,6],[7,8,10,11,12],[9],[13]],
        [[0],[1,2,3],[4,7,5,6,8,9,10,11,12],[13]],
        [[0],[1,2,3],[4,7,5,6,8,9,10],[11,12],[13]],
        [[0],[1,2,3],[4,7,5,6,9],[8,10,11,12],[13]],
        [[0,4,7],[1,2,3],[5,9],[6,8,10,11],[12],[13]],
        [7],
        [[5,9],[0],[4,6,10],[1,2,3],[8,11],[12,13]],
        [12],
        [[0,5,9],[1,2,3],[4,6,10,8,11],[13]],
        [[0],[5,9],[1,2],[3,11],[4,6,10,8],[13]],
        [11],
        [[0],[5,9],[1,2,10],[3,6],[4,8],[13]],
        [10],
        [[0],[1,2,9],[5,6],[3],[4,8],[13]],
        [[0],[1],[9,5,6],[3],[4,8],[2],[13]],
        [[0,1,9,5,6,3],[4,8],[2],[13]],
        [1,3],
        [[0],[9,5,6,3,4,8],[2],[13]]
    ]
    plot_data(*parse_data(chars, events))

Hah, aspetto xkcd molto bello:) ... hai qualche possibilità di etichettare le linee?
Martin Ender,

Etichetta le linee, hanno una larghezza delle linee diversa (con una diminuzione / aumento tra alcuni punti) e infine ... rendi le linee più orizzontali vicino a un vertice durante l'interpolazione, più come una curva più bezier e questa sarebbe la migliore voce IMO: )
Ottimizzatore il

1
Grazie, ma lo stile xkcd è incluso in matplotlib, quindi era solo una chiamata di funzione :) Beh, ho creato una legenda, ma occupava quasi un terzo dell'immagine, quindi l'ho commentato.
Pgy

Ho modificato la mia risposta, penso che ora stia meglio.
Pgy

6

T-SQL

Non sono contento di questo come una voce, ma penso che questa domanda meriti almeno una prova. Cercherò di migliorare questa volta in seguito permettendo, ma l'etichettatura sarà sempre un problema in SQL. La soluzione richiede SQL 2012+ ed è eseguita in SSMS (SQL Server Management Studio). L'output si trova nella scheda dei risultati spaziali.

-- Variables for the input
DECLARE @actors NVARCHAR(MAX) = '["T-Rex", "Raptor", "Raptor", "Raptor", "Malcolm", "Grant", "Sattler", "Gennaro", "Hammond", "Kids", "Muldoon", "Arnold", "Nedry", "Dilophosaurus"]';
DECLARE @timeline NVARCHAR(MAX) = '
[
   [[1], [2, 3, 4], [5], [6, 7], [8, 9, 11, 12, 13], [10], [14]],
   [[1], [2, 3, 4], [5, 8, 6, 7, 9, 10, 11, 12, 13], [14]],
   [[1], [2, 3, 4], [5, 8, 6, 7, 9, 10, 11], [12, 13], [14]],
   [[1], [2, 3, 4], [5, 8, 6, 7, 10], [9, 11, 12, 13], [14]],
   [[1, 5, 8], [2, 3, 4], [6, 10], [7, 9, 11, 12], [13], [14]],
   [8],
   [[6, 10], [1], [5, 7, 11], [2, 3, 4], [9, 12], [13, 14]],
   [13],
   [[1, 6, 10], [2, 3, 4], [5, 7, 11, 9, 12], [14]],
   [[1], [6, 10], [2, 3], [4, 12], [5, 7, 11, 9], [14]],
   [12],
   [[1], [6, 10], [2, 3, 11], [4, 7], [5, 9], [14]],
   [11],
   [[1], [2, 3, 10], [6, 7], [4], [5, 9], [14]],
   [[1], [2], [10, 6, 7], [4], [5, 9], [3], [14]],
   [[1, 2, 10, 6, 7, 4], [5, 9], [3], [14]],
   [2, 4],
   [[1], [10, 6, 7, 5, 9], [3], [14]]
]
';

-- Populate Actor table
WITH actor(A) AS ( SELECT CAST(REPLACE(STUFF(REPLACE(REPLACE(@actors,', ',','),'","','</a><a>'),1,2,'<a>'),'"]','</a>') AS XML))
SELECT ROW_NUMBER() OVER (ORDER BY(SELECT \)) ActorID, a.n.value('.','varchar(50)') Name
INTO Actor
FROM actor CROSS APPLY A.nodes('/a') as a(n);

-- Populate Timeline Table
WITH Seq(L) AS (
    SELECT CAST(REPLACE(REPLACE(REPLACE(REPLACE(@timeline,'[','<e>'),']','</e>'),'</e>,<e>','</e><e>'),'</e>,','</e>') AS XML)
    ),
    TimeLine(N,Exerpt,Elem) AS (
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) N
        ,z.query('.')
        ,CAST(REPLACE(CAST(z.query('.') AS VARCHAR(MAX)),',','</e><e>') AS XML)
    FROM Seq 
        CROSS APPLY Seq.L.nodes('/e/e') AS Z(Z)
    ),
    Groups(N,G,Exerpt) AS (
    SELECT N, 
        ROW_NUMBER() OVER (PARTITION BY N ORDER BY CAST(SUBSTRING(node.value('.','varchar(50)'),1,ISNULL(NULLIF(CHARINDEX(',',node.value('.','varchar(50)')),0),99)-1) AS INT)), 
        CAST(REPLACE(CAST(node.query('.') AS VARCHAR(MAX)),',','</e><e>') AS XML) C
    FROM TimeLine 
        CROSS APPLY Exerpt.nodes('/e/e') as Z(node)
    WHERE Exerpt.exist('/e/e') = 1
    )
SELECT * 
INTO TimeLine
FROM (
    SELECT N, null G, null P, node.value('.','int') ActorID, 1 D 
    FROM TimeLine CROSS APPLY TimeLine.Elem.nodes('/e') AS E(node)
    WHERE Exerpt.exist('/e/e') = 0
    UNION ALL
    SELECT N, G, DENSE_RANK() OVER (PARTITION BY N, G ORDER BY node.value('.','int')), node.value('.','int') ActorID, 0
    FROM Groups CROSS APPLY Groups.Exerpt.nodes('/e') AS D(node)
    ) z;

-- Sort the entries again
WITH ReOrder AS (
            SELECT *, 
                ROW_NUMBER() OVER (PARTITION BY N,G ORDER BY PG, ActorID) PP, 
                COUNT(P) OVER (PARTITION BY N,G) CP, 
                MAX(G) OVER (PARTITION BY N) MG, 
                MAX(ActorID) OVER (ORDER BY (SELECT\)) MA
            FROM (
                SELECT *,
                    LAG(G,1) OVER (PARTITION BY ActorID ORDER BY N) PG,
                    LEAD(G,1) OVER (PARTITION BY ActorID ORDER BY N) NG
                FROM timeline
                ) rg
    )
SELECT * INTO Reordered
FROM ReOrder;
ALTER TABLE Reordered ADD PPP INT
GO
ALTER TABLE Reordered ADD LPP INT
GO
WITH U AS (SELECT N, P, LPP, LAG(PP,1) OVER (PARTITION BY ActorID ORDER BY N) X FROM Reordered)
UPDATE U SET LPP = X FROM U;
WITH U AS (SELECT N, ActorID, P, PG, LPP, PPP, DENSE_RANK() OVER (PARTITION BY N,G ORDER BY PG, LPP) X FROM Reordered)
UPDATE U SET PPP = X FROM U;
GO

SELECT Name, 
    Geometry::STGeomFromText(
        STUFF(LS,1,2,'LINESTRING (') + ')'
        ,0)
        .STBuffer(.1)
        .STUnion(
        Geometry::STGeomFromText('POINT (' + REVERSE(SUBSTRING(REVERSE(LS),1,CHARINDEX(',',REVERSE(LS))-1)) + ')',0).STBuffer(D*.4)
        )
FROM Actor a
    CROSS APPLY (
        SELECT CONCAT(', '
            ,((N*5)-1.2)
                ,' ',(G)+P
            ,', '
            ,((N*5)+1.2)
                ,' ',(G)+P 
            ) AS [text()]
        FROM (
            SELECT ActorID, N,
                CASE WHEN d = 1 THEN
                    ((MA+.0) / (LAG(MG,1) OVER (PARTITION BY ActorID ORDER BY N)+.0)) * 
                    PG * 1.2
                ELSE 
                    ((MA+.0) / (MG+.0)) * 
                    G * 1.2
                END G,
                CASE WHEN d = 1 THEN
                (LAG(PPP,1) OVER (PARTITION BY ActorID ORDER BY N) -((LAG(CP,1) OVER (PARTITION BY ActorID ORDER BY N)-1)/2)) * .2 
                ELSE
                (PPP-((CP-1)/2)) * .2 
                END P
                ,PG
                ,NG
            FROM Reordered
            ) t
        WHERE a.actorid = t.actorid
        ORDER BY N, G
        FOR XML PATH('')
        ) x(LS)
    CROSS APPLY (SELECT MAX(D) d FROM TimeLine dt WHERE dt.ActorID = a.ActorID) d
GO

DROP TABLE Actor;
DROP TABLE Timeline;
DROP TABLE Reordered;

La sequenza temporale risultante è simile alla seguente inserisci qui la descrizione dell'immagine


4

Mathematica, soluzione di riferimento

Per riferimento, fornisco uno script Mathematica che soddisfa esattamente i requisiti minimi, niente di più, niente di meno.

Si aspetta che i personaggi siano un elenco del formato nella domanda in charse gli eventi in events.

n = Length@chars;
m = Max@Map[Length, events, {2}];
deaths = {};
Graphics[
 {
  PointSize@Large,
  (
     linePoints = If[Length@# == 3,
         lastPoint = {#[[1]], #[[2]] + #[[3]]/(m + 2)},
         AppendTo[deaths, Point@lastPoint]; lastPoint
         ] & /@ Position[events, #];
     {
      Line@linePoints,
      Text[chars[[#]], linePoints[[1]] - {.5, 0}]
      }
     ) & /@ Range@n,
  deaths
  }
 ]

Ad esempio, ecco l'esempio di Jurassic Park usando il tipo di elenco di Mathematica:

chars = {"T-Rex", "Raptor", "Raptor", "Raptor", "Malcolm", "Grant", 
   "Sattler", "Gennaro", "Hammond", "Kids", "Muldoon", "Arnold", 
   "Nedry", "Dilophosaurus"};
events = {
   {{1}, {2, 3, 4}, {5}, {6, 7}, {8, 9, 11, 12, 13}, {10}, {14}},
   {{1}, {2, 3, 4}, {5, 8, 6, 7, 9, 10, 11, 12, 13}, {14}},
   {{1}, {2, 3, 4}, {5, 8, 6, 7, 9, 10, 11}, {12, 13}, {14}},
   {{1}, {2, 3, 4}, {5, 8, 6, 7, 10}, {9, 11, 12, 13}, {14}},
   {{1, 5, 8}, {2, 3, 4}, {6, 10}, {7, 9, 11, 12}, {13}, {14}},
   {8},
   {{6, 10}, {1}, {5, 7, 11}, {2, 3, 4}, {9, 12}, {13, 14}},
   {13},
   {{1, 6, 10}, {2, 3, 4}, {5, 7, 11, 9, 12}, {14}},
   {{1}, {6, 10}, {2, 3}, {4, 12}, {5, 7, 11, 9}, {14}},
   {12},
   {{1}, {6, 10}, {2, 3, 11}, {4, 7}, {5, 9}, {14}},
   {11},
   {{1}, {2, 3, 10}, {6, 7}, {4}, {5, 9}, {14}},
   {{1}, {2}, {10, 6, 7}, {4}, {5, 9}, {3}, {14}},
   {{1, 2, 10, 6, 7, 4}, {5, 9}, {3}, {14}},
   {2, 4},
   {{1}, {10, 6, 7, 4, 5, 9}, {3}, {14}}
};

otterremo:

inserisci qui la descrizione dell'immagine

(Fare clic per una versione più grande.)

Non sembra troppo male, ma principalmente perché i dati di input sono più o meno ordinati. Se mescoliamo gruppi e personaggi in ciascun evento (mantenendo la stessa struttura), possono accadere cose del genere:

inserisci qui la descrizione dell'immagine

Il che è un po 'un casino.

Quindi, come ho detto, questo soddisfa solo i requisiti minimi. Non cerca di trovare un bel layout e non è carino, ma è qui che entrate voi ragazzi!


Ho solo pensato che potresti forse "prettificarlo" usando spline quadratiche o cubiche per rimuovere gli spigoli vivi? (Lo farei in questo modo in cui la tangente in determinati punti è sempre 0)
flawr

@flawr Certo, oppure potrei applicare alcuni di questi trucchi , ma quello non era lo scopo di questa risposta. ;) Volevo davvero solo fornire un riferimento per il minimo assoluto.
Martin Ender,

3
Oh scusa, non ho nemmeno notato che questa era la tua domanda = P
flawr
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.