Xgboost: [Neues Feature] Monotone Abhängigkeiten in der Baumkonstruktion

Erstellt am 27. Aug. 2016  ·  46Kommentare  ·  Quelle: dmlc/xgboost

Ich habe einige Anfragen zur Unterstützung monotoner Einschränkungen für bestimmte Funktionen in Bezug auf die Ausgabe erhalten.

dh wenn andere Merkmale festgelegt sind, erzwingen Sie eine monoton ansteigende Vorhersage bezüglich des bestimmten spezifizierten Merkmals. Ich öffne diese Ausgabe, um das allgemeine Interesse an dieser Funktion zu erfahren. Ich kann dies hinzufügen, wenn genügend Interesse besteht,

Ich benötige die Hilfe von Freiwilligen aus der Community, um die Beta-Funktion zu testen und ein Dokument und ein Tutorial zur Verwendung dieser Funktion beizutragen. Bitte beantworten Sie das Problem, wenn Sie interessiert sind

Hilfreichster Kommentar

Derzeit ist diese Funktion nicht in der Sklearn-API enthalten. Können Sie oder jemand bitte helfen, es hinzuzufügen? Danke!

Alle 46 Kommentare

Eine experimentelle Version wird unter https://github.com/dmlc/xgboost/pull/1516 bereitgestellt https://github.com/tqchen/xgboost .

Schalten Sie die folgenden Optionen ein (wahrscheinlich über Python, r API möglich)

monotone_constraints = "(0,1,1,0)"

Es gibt zwei Argumente

  • monotone_constraints ist eine Liste in der Länge der Anzahl von Features, 1 bedeutet monoton ansteigend, - 1 bedeutet abnehmend, 0 bedeutet keine Einschränkung. Wenn es kürzer als die Anzahl der Merkmale ist, wird 0 aufgefüllt.

    • Derzeit unterstützt es das Tupelformat von Python. Sie können Dinge als String übergeben, wenn Sie r . verwenden

Dinge zu überprüfen

  • [x] Die Geschwindigkeit der ursprünglichen Baum-Booster verlangsamt sich nicht (ich habe die Codestruktur ein wenig geändert, theoretisch wird die Vorlagenoptimierung sie einreihen, muss aber bestätigt werden)
  • [x] Die Geschwindigkeit und Richtigkeit der monotonen Regression
  • [x] Die Leistung durch Einführung dieser Einschränkung

Bekannte Einschränkungen

Unterstützt derzeit nur den exakten gierigen Algorithmus auf Multi-Core. Noch nicht in der verteilten Version verfügbar

@tqchen Ich habe heute bei der Arbeit eine Anfrage erhalten, einige GBMs mit monotonen Einschränkungen zu bauen, um die Leistung einiger anderer Modelle zu testen. Dies wäre mit einem Tweedie-Abweichungsverlust, also müsste ich mit einer benutzerdefinierten Verlustfunktion gehen, wie sie heute steht.

Auf jeden Fall scheint es eine gute Gelegenheit zu sein, zu helfen und gleichzeitig etwas zu erledigen.

Basierend auf der Diskussion hier erzwingt GBM(R Package) Monotonie nur lokal.
Könnten Sie klarstellen, wie XGBoost monotone Einschränkungen erzwingt?
Es wäre großartig, wenn XGBoost globale Einschränkungen durchsetzen könnte.

Ich verstehe nicht, was Sie mit lokaler oder globaler Einschränkung meinen, können Sie das näher erläutern?

Entschuldigung, ich habe den falschen Link eingefügt, hier ist der richtige (Link)
Jeder Baum kann nur in einer bestimmten Teilmenge des interessierenden Merkmals einer monotonen Beschränkung folgen, so dass viele Baumensembles zusammen eine Verletzung der Gesamtmonotonie im gesamten Bereich dieses Merkmals verursachen können.

OK, nach meinem Verständnis wird es global durchgesetzt. Sie sind herzlich eingeladen, es auszuprobieren.

Habe gerade einige einfache Tests der Monotonie-Beschränkung im Kontext einer univariaten Regression durchgeführt. Den Code und eine sehr kurze Dokumentation finden Sie hier:

https://github.com/XiaoxiaoWang87/xgboost_mono_test/blob/master/xgb_monotonicity_constraint_testing1-univariate.ipynb

Einige erste Beobachtungen:

  • Für ein Regressionsproblem mit einer einzelnen Variablen scheint die monotone Einschränkung = +1 gut zu funktionieren
  • Für ein Regressionsproblem mit einer einzelnen Variablen scheint in meinem Datensatz die monotone Einschränkung = -1 keine monoton fallende Funktion zu ergeben. Es gibt vielmehr eine Konstante. Dies kann aber auch an der fehlenden Verbesserung beim Forcen des Constraints liegen. Zur Bestätigung (pro Tianqis Vorschlag versuchen Sie, den Datensatz umzudrehen und die Einschränkung auf +1 zu setzen).
  • Das (richtige) Hinzufügen der Einschränkung kann möglicherweise eine Überanpassung verhindern und einen gewissen Leistungs-/Interpretationsvorteil bringen.

Es stellte sich heraus, dass ich im Fall von Constraint = -1 einen Fehler einführe. Ich habe einen Fix verschoben, bitte sehen Sie, ob die neueste Version gut funktioniert. Bitte überprüfen Sie auch, ob es funktioniert, wenn mehrere Einschränkungen vorliegen

@tqchen Ich habe Ihren Fix auf den abnehmenden Fehler getestet, scheint jetzt zu funktionieren.

xgboost-no-constraint
xgboost-with-constraint

Lassen Sie uns bestätigen, ob bei einigen Standarddatensätzen die Geschwindigkeit gegenüber der Originalversion abnimmt, dann können wir sie zusammenführen

@tqchen Ich habe ein Modell mit zwei Variablen getestet, eines mit einer zunehmenden Einschränkung und eines mit einer abnehmenden:

params_constrained = params.copy()
params_constrained['updater'] = "grow_monotone_colmaker,prune"
params_constrained['monotone_constraints'] = "(1,-1)"

Die Ergebnisse sind gut

xgboost-two-vars-increasing
xgboost-two-vars-decreasing

Ich werde versuchen, heute Nachmittag ein wenig Zeit zu finden, um ein paar Timing-Tests zu machen.

Ich habe ein Update auf #1516 vorgenommen, um die automatische Erkennung von Montone-Optionen zu ermöglichen. Jetzt muss der Benutzer nur noch monotone_constraints = "(0,1,1,0)" eingeben, bitte überprüfen Sie, ob es funktioniert.

Ich werde dies einbinden, wenn die Geschwindigkeitstests in Ordnung sind, und lassen Sie uns zur nächsten Phase des Hinzufügens von Tutorials übergehen

@madrury @XiaoxiaoWang87

Tests für den multivariaten Fall hier hinzugefügt:

https://github.com/XiaoxiaoWang87/xgboost_mono_test/blob/master/xgb_monotonicity_constraint_testing2-multivariate.ipynb

  • Ich bestätige jetzt, dass sowohl die monotone Einschränkung = 1 als auch = -1 wie erwartet funktioniert.
  • Einschränken der Monotonie führt nicht zu einer offensichtlichen Verschlechterung der Geschwindigkeit*
    *speed = avg [Zeit bis zum frühen Stoppen / Anzahl der Boosting-Iterationen bis zum frühen Stoppen]

no constraint: 964.9 microseconds per iteration
with constraint: 861.7 microseconds per iteration

(bitte kommentieren, wenn Sie eine bessere Möglichkeit haben, den Geschwindigkeitstest durchzuführen)

  • Seien Sie vorsichtig, wenn Sie die Richtung für eine nicht monotone Variable einschränken. Dies kann zu Leistungseinbußen führen.
  • Code-Absturz wegen Check failed: (wleft) <= (wright) beim Spielen mit verschiedenen Hyperparametern zu sehen.

Ich habe ein paar Timing-Experimente in einem Jupyter-Notebook durchgeführt.

Erster Test: einige einfache simulierte Daten. Es gibt zwei Merkmale, eine ansteigende und eine abnehmende, aber mit einer kleinen Sinuswelle überlagert, so dass jede Funktion nicht wirklich monoton ist

X = np.random.random(size=(N, K))
y = (5*X[:, 0] + np.sin(5*2*pi*X[:, 0])
     - 5*X[:, 1] - np.cos(5*2*pi*X[:, 1])
     + np.random.normal(loc=0.0, scale=0.01, size=N))

Hier sind Timing-Ergebnisse von xgboosts mit und ohne monotone Einschränkungen. Ich habe das frühe Stoppen deaktiviert und für jeden eine bestimmte Anzahl von Iterationen erhöht.

Zuerst ohne monotone Einschränkungen:

%%timeit -n 100
model_no_constraints = xgb.train(params, dtrain, 
                                 num_boost_round = 2500, 
                                 verbose_eval = False)

100 loops, best of 3: 246 ms per loop

Und hier mit Monotonie-Einschränkungen

%%timeit -n 100
model_with_constraints = xgb.train(params_constrained, dtrain, 
                                 num_boost_round = 2500, 
                                 verbose_eval = False)

100 loops, best of 3: 196 ms per loop

Zweiter Test: Kalifornische Wohnungsdaten von sklearn. Ohne Einschränkungen

%%timeit -n 10
model_no_constraints = xgb.train(params, dtrain, 
                                 num_boost_round = 2500, 
                                 verbose_eval = False)

10 loops, best of 3: 5.9 s per loop

Hier sind die Einschränkungen, die ich verwendet habe

print(params_constrained['monotone_constraints'])

(1,1,1,0,0,1,0,0)

Und das Timing für das eingeschränkte Modell

%%timeit -n 10
model_no_constraints = xgb.train(params, dtrain, 
                                 num_boost_round = 2500, 
                                 verbose_eval = False)

10 loops, best of 3: 6.08 s per loop

@XiaoxiaoWang87 Ich habe einen anderen PR gedrängt, den Scheck auf wleft und wright zu verlieren, bitte sehen Sie, dass es funktioniert.
@madrury Können Sie auch ohne die Einschränkungsfunktion mit einer früheren Version von XGBoost vergleichen?

@tqchen Klar. Können Sie einen Commit-Hash zum Vergleich empfehlen? Soll ich einfach den Commit verwenden, bevor Sie die monotonen Einschränkungen hinzufügen?

Ja der vorherige wird reichen

@tqchen Beim erhalte ich einige Fehler, die ich vorher nicht hatte. Ich hoffe, der Grund fällt Ihnen klar auf.

Wenn ich versuche, denselben Code wie zuvor auszuführen, erhalte ich eine Ausnahme, hier ist der vollständige Traceback:

XGBoostError                              Traceback (most recent call last)
<ipython-input-14-63a9f6e16c9a> in <module>()
      8    model_with_constraints = xgb.train(params, dtrain, 
      9                                        num_boost_round = 1000, evals = evallist,
---> 10                                    early_stopping_rounds = 10)  

/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/training.pyc in train(params, dtrain, num_boost_round, evals, obj, feval, maximize, early_stopping_rounds, evals_result, verbose_eval, learning_rates, xgb_model, callbacks)
    201                            evals=evals,
    202                            obj=obj, feval=feval,
--> 203                            xgb_model=xgb_model, callbacks=callbacks)
    204 
    205 

/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/training.pyc in _train_internal(params, dtrain, num_boost_round, evals, obj, feval, xgb_model, callbacks)
     72         # Skip the first update if it is a recovery step.
     73         if version % 2 == 0:
---> 74             bst.update(dtrain, i, obj)
     75             bst.save_rabit_checkpoint()
     76             version += 1

/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/core.pyc in update(self, dtrain, iteration, fobj)
    804 
    805         if fobj is None:
--> 806             _check_call(_LIB.XGBoosterUpdateOneIter(self.handle, iteration, dtrain.handle))
    807         else:
    808             pred = self.predict(dtrain)

/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/core.pyc in _check_call(ret)
    125     """
    126     if ret != 0:
--> 127         raise XGBoostError(_LIB.XGBGetLastError())
    128 
    129 

XGBoostError: [14:08:41] src/tree/tree_updater.cc:18: Unknown tree updater grow_monotone_colmaker

Wenn ich alles für das von Ihnen implementierte Schlüsselwortargument auswechsele, erhalte ich ebenfalls eine Fehlermeldung:

TypeError                                 Traceback (most recent call last)
<ipython-input-15-ef7671f72925> in <module>()
      8                                    monotone_constraints="(1)",
      9                                    num_boost_round = 1000, evals = evallist,
---> 10                                    early_stopping_rounds = 10)  

TypeError: train() got an unexpected keyword argument 'monotone_constraints'

Entfernen Sie das Updater-Argument und behalten Sie die monotonen Einschränkungsargumente in Parametern bei, da der monotone Einschränkungs-Updater jetzt automatisch aktiviert wird, wenn monotone Einschränkungen angezeigt werden

@tqchen Mein Kumpel @amontz hat mir sofort nach dem monotone_constraints als Kwarg an .train .

Es funktioniert mit diesen Anpassungen. Danke.

@madrury kannst du die Geschwindigkeit bestätigen?

Auch @madrury und @XiaoxiaoWang87 Da diese Funktion jetzt kurz vor der Zusammenführung steht, wäre es großartig, wenn Sie sich koordinieren könnten, um ein Tutorial zu erstellen, das den Benutzern diese Funktion vorstellt.

Wir können ipy notebook nicht direkt zum Hauptrepo bringen. Bilder können jedoch auf https://github.com/dmlc/web-data/tree/master/xgboost und Markdown zum Hauptrepo verschoben werden.

Wir müssen auch die Stringkonvertierung der Front-End-Schnittstelle ändern, damit das int-Tupel in das String-Tupelformat konvertiert werden kann, das vom Backend akzeptiert wird.

@hetong007 für Änderungen in R und @slundberg für Julia

@tqchen Julia ist derzeit an die 0.4-Version von XGBoost angehängt, also wenn ich es das nächste Mal verwenden muss und Zeit habe, werde ich die Bindungen aktualisieren, wenn bis dahin niemand anderes hat. An diesem Punkt kann diese Änderung auch hinzugefügt werden.

Hier ist der Vergleich zwischen Modellen _ohne_ einer monotonen Einschränkung von vor der Implementierung bis nachher.

Commit 8cac37 : Vor der Implementierung der monotonen Einschränkung.'
Simulierte Daten : 100 loops, best of 3: 232 ms per loop
Kalifornien-Daten : 10 loops, best of 3: 5.89 s per loop

Commit b1c224 : Nach der Implementierung der monotonen Einschränkung.
Simulierte Daten : 100 loops, best of 3: 231 ms per loop
Kalifornien-Daten : 10 loops, best of 3: 5.61 s per loop

Die Beschleunigung für Kalifornien nach der Implementierung erscheint mir verdächtig, aber ich habe es zweimal versucht, und es ist konsistent.

Ich würde gerne versuchen, ein Tutorial zu schreiben. Ich werde mich in der vorhandenen Dokumentation umschauen und in den nächsten Tagen etwas zusammenstellen.

Das ist toll, die PR ist jetzt offiziell mit dem Master verschmolzen. Freue mich auf das Tutorial

Danke @madrury. Ich freue mich auf. Lassen Sie mich wissen, was ich helfen kann. Über weitere Studien zu diesem Thema würde ich mich auf jeden Fall freuen.

Ich werde es morgen verbessern. Ich bin nur neugierig auf den Grund der Kommunikation mit C++ über einen String anstelle eines Arrays.

Ich teste von R. Ich habe zufällig Daten mit zwei Variablen generiert und versuche, Vorhersagen zu treffen.

Das habe ich jedoch gefunden

  1. xgboost schränkt die Vorhersage nicht ein.
  2. der Parameter monotone_constraints macht die Vorhersage etwas anders.

Bitte weisen Sie darauf hin, wenn ich Fehler gemacht habe.

Der Code, um es zu reproduzieren (getestet auf der neuesten Github-Version , nicht von drat ):

set.seed(1024)
x1 = rnorm(1000, 10)
x2 = rnorm(1000, 10)
y = -1*x1 + rnorm(1000, 0.001) + 3*sin(x2)
train = cbind(x1, x2)

bst = xgboost(data = train, label = y, max_depth = 2,
                   eta = 0.1, nthread = 2, nrounds = 10,
                   monotone_constraints = '(1,-1)')

pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'with constraint')
pred.ord = pred[order(train[,1])]
lines(pred.ord)

wc

bst = xgboost(data = train, label = y, max_depth = 2,
                   eta = 0.1, nthread = 2, nrounds = 10)

pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'without constraint')
pred.ord = pred[order(train[,1])]
lines(pred.ord)

woc

Die Einschränkung wurde für die Teilbestellung durchgeführt. Die Beschränkung wird also nur erzwungen, wenn wir die Montonachse bewegen und die andere Achse festhalten

@hetong007 Um meine Pläne zu machen, habe ich

  • Erstellt ein Array mit dem Raster der x-Koordinaten, bei dem ich diese Variable vorhersagen wollte, und füge es dann in das Liniendiagramm ein. Dies würde seq in R verwenden.
  • Setzen Sie alle anderen Variablen gleich ihrem Durchschnittswert in den Trainingsdaten. Dies wäre etwa colmeans in R.

Hier ist der Python-Code, den ich für die oben eingefügten Plots verwendet habe. Er sollte ziemlich leicht in einen entsprechenden R-Code konvertiert werden.

def plot_one_feature_effect(model, X, y, idx=1):

    x_scan = np.linspace(0, 1, 100)    
    X_scan = np.empty((100, X.shape[1]))
    X_scan[:, idx] = x_scan

    left_feature_means = np.tile(X[:, :idx].mean(axis=0), (100, 1))
    right_feature_means = np.tile(X[:, (idx+1):].mean(axis=0), (100, 1))
    X_scan[:, :idx] = left_feature_means
    X_scan[:, (idx+1):] = right_feature_means

    X_plot = xgb.DMatrix(X_scan)
    y_plot = model.predict(X_plot, ntree_limit=bst.best_ntree_limit)

    plt.plot(x_scan, y_plot, color = 'black')
    plt.plot(X[:, idx], y, 'o', alpha = 0.25)

So mache ich die partiellen Abhängigkeitsdiagramme (für ein beliebiges Modell):

  • Scannen Sie ein Werteraster nach Merkmal X.
  • Für jeden Rasterwert von Merkmal X:

    • Setzen Sie die gesamte Feature-X-Spalte (alle Zeilen) auf diesen Wert. Andere Funktionen unverändert.

    • Machen Sie Vorhersagen für alle Zeilen.

    • Nehmen Sie den Durchschnitt der Vorhersage.

  • Die resultierenden (X-Feature-Wert, durchschnittliche Vorhersage)-Paare geben Ihnen die X-Feature-Teilabhängigkeit.

Code:

def plot_partial_dependency(bst, X, y, f_id):

    X_temp = X.copy()

    x_scan = np.linspace(np.percentile(X_temp[:, f_id], 0.1), np.percentile(X_temp[:, f_id], 99.5), 50)
    y_partial = []

    for point in x_scan:

        X_temp[:, f_id] = point

        dpartial = xgb.DMatrix(X_temp[:, feature_ids])
        y_partial.append(np.average(bst.predict(dpartial)))

    y_partial = np.array(y_partial)

    # Plot partial dependence

    fig, ax = plt.subplots()
    fig.set_size_inches(5, 5)
    plt.subplots_adjust(left = 0.17, right = 0.94, bottom = 0.15, top = 0.9)

    ax.plot(x_scan, y_partial, '-', color = 'black', linewidth = 1)
    ax.plot(X[:, f_id], y, 'o', color = 'blue', alpha = 0.02)

    ax.set_xlim(min(x_scan), max(x_scan))
    ax.set_xlabel('Feature X', fontsize = 10)    
    ax.set_ylabel('Partial Dependence', fontsize = 12)

Danke für die Anleitung! Mir wurde klar, dass ich einen dummen Fehler in der Handlung gemacht hatte. Hier ist ein weiterer Test für univariate Daten, der Plot scheint in Ordnung zu sein:

set.seed(1024)
x = rnorm(1000, 10)
y = -1*x + rnorm(1000, 0.001) + 3*sin(x)
train = matrix(x, ncol = 1)

bst = xgboost(data = train, label = y, max_depth = 2,
               eta = 0.1, nthread = 2, nrounds = 100,
               monotone_constraints = '(-1)')
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'with constraint', pch=20)
lines(train[ind,1], pred.ord, col=2, lwd = 5)

rplot

bst = xgboost(data = train, label = y, max_depth = 2,
               eta = 0.1, nthread = 2, nrounds = 100)
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'without constraint', pch=20)
lines(train[ind,1], pred.ord, col=2, lwd = 5)

woc

@hetong007 Das Ziel in der R-Schnittstelle besteht also darin, dem Benutzer die Möglichkeit zu geben, neben den Strings das R-Array zu übergeben

monotone_constraints=c(1,-1)

Bitte lassen Sie es uns wissen, wenn Sie PR für das Tutorial machen

@hetong007 Sie sind auch mehr als willkommen, eine R-Blogger-Version zu

@tqchen Sorry Leute, ich war die Woche auf Dienstreise.

Ich habe ein paar Pull-Requests mit für ein Tutorial zu monotonen Einschränkungen gesendet. Bitte teilen Sie mir Ihre Meinung mit, ich freue mich über jede Kritik oder Kritik.

Hoffentlich ist es angebracht, dies hier zu fragen: Funktioniert das jetzt, wenn wir mit den üblichen git clone --recursive https://github.com/dmlc/xgboost aktualisieren?

Ich frage, wie ich das neue Tutorial gesehen habe, aber nichts Neues über eine Änderung des Codes selbst. Danke euch allen!

Ja, die neue Funktion wird zusammengeführt, bevor das Tutorial zusammengeführt wird

Hallo,

Ich bin mir nicht sicher, ob Sie die globale Monotonie erfolgreich implementiert haben, von dem, was ich in Ihrem Code gesehen habe, entspricht es eher einer lokalen Monotonie.

Hier ist ein einfaches Beispiel, das Monotonie bricht:

`
df <- data.frame(y = c(2,rep(6,100),1,rep(11,100)),
x1= c(rep(1,101),rep(2,101)),x2 = c(1,rep(2,100),1,rep(2,100)))

Bibliothek (xgboost)
set.saat(0)
XGB <- xgboost(data=data.matrix(df[,-1]),label=df[,1],
objektiv=" reg:linear ",
bag.fraction=1,nround=100,monotone_constraints=c(1,0),
eta=0,1 )

sans_corr <- data.frame(x1=c(1,2,1,2),x2=c(1,1,2,2))

sans_corr$prediction <- Predict(XGB,data.matrix(sans_corr))
`

Ich hoffe, mein Verständnis Ihres Codes und meines Beispiels ist nicht falsch

Derzeit ist diese Funktion nicht in der Sklearn-API enthalten. Können Sie oder jemand bitte helfen, es hinzuzufügen? Danke!

Ist es möglich, einer Variablen eine allgemeine Monotonie zu erzwingen, ohne anzugeben, ob sie steigend oder fallend sein soll?

@davidADSP Sie können eine Spearman-Korrelationsprüfung für den gewünschten Prädiktor und das gewünschte Ziel durchführen, um zu sehen, ob eine Erhöhung oder Verringerung richtig ist.

Diese Funktion scheint ungültig zu sein, wenn 'tree_method':'hist'. @tqchen Hilfe? Danke an alle.

Wie funktioniert die Einschränkung für ein Mehrklassenziel wie mlogloss? Wird die Einschränkung der Monotonie für Mehrklassenverluste unterstützt? Wenn ja, wie wird es durchgesetzt. (Für jede Klasse gibt es einen Baum)

Gibt es ein Whitepaper zum Monotizitätsalgorithmus, der in XGBOOST erzwungen wird? Ist es global oder lokal? Lokal bedeutet spezifisch für bestimmte Knoten, aber Knoten in anderen Teilen des Baums können eine Verletzung der Gesamtmonotonie verursachen. Kann mir bitte auch jemand helfen, die Zeile L412-417 zu verstehen. Warum "w" begrenzt ist - nach oben und unten. Wie dies hilft, die Monotonie aufrechtzuerhalten. Zeile 457 - Warum wird "mid" verwendet?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

hx364 picture hx364  ·  3Kommentare

frankzhangrui picture frankzhangrui  ·  3Kommentare

nicoJiang picture nicoJiang  ·  4Kommentare

pplonski picture pplonski  ·  3Kommentare

XiaoxiaoWang87 picture XiaoxiaoWang87  ·  3Kommentare