1. 程式人生 > >在Hexo中渲染MathJax數學公式

在Hexo中渲染MathJax數學公式

sage 範圍 dot libs 上下 n+1 決定 html標簽 ges

最近學機器學習涉及很多的數學公式,公式如果用截圖顯示,會比較low而且不方便。因此需要對Hexo做些配置,支持公式渲染。同時文末整理了各種公式的書寫心得,比如矩陣、大小括號、手動編號、上下角標和多行對其等,有興趣的可以看看。


通過hexo-math插件安裝MathJax

有個插件hexo-math,可以給Hexo博客添加MathJax公式支持,GitHub地址 https://github.com/hexojs/hexo-math

安裝方法可其他hexo插件一樣,在博客根目錄執行npm install hexo-math --save安裝,配置見GitHub說明頁,這裏我沒有通過這種方式安裝,而是直接在主題配置中添加MathJax的js來安裝的。


在主題中手動添加js安裝MathJax

類似所有第三方js插件,js加載方式有兩種:

  • 第一種,通過連接CDN加載js代碼。好處是省了本地配置js代碼,並且每次加載都是最新的,缺點是一旦連接的CDN出問題,可能卡住頁面的js加載。
  • 第二種,將js代碼下載下來,放到主題的js文件夾中,通過本地相對目錄加載。優缺點和第一種方法正相反。

這裏我選擇通過CDN加載,因為把代碼下載下來後發現有好多js,搞不清楚其中的引用關系,還是直接用官方給出的通過CDN加載的簡便方法吧:Getting Started with MathJax

又綜合了網上其他人給出的一些配置,最終代碼如下。

在themes/free2mind/layout/_partial 目錄中新建mathjax.ejs,填入如下js代碼:

<!-- MathJax配置,可通過單美元符號書寫行內公式等 -->
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
    "HTML-CSS": { 
        preferredFont: "TeX", 
        availableFonts: ["STIX","TeX"], 
        linebreaks: { automatic:true }, 
        EqnChunk
: (MathJax.Hub.Browser.isMobile ? 10 : 50) }, tex2jax: { inlineMath: [ ["$", "$"], ["\\(","\\)"] ], processEscapes: true, ignoreClass: "tex2jax_ignore|dno", skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'] }, TeX: { equationNumbers: { autoNumber: "AMS" }, noUndefined: { attributes: { mathcolor: "red", mathbackground: "#FFEEEE", mathsize: "90%" } }, Macros: { href: "{}" } }, messageStyle: "none" }); </script> <!-- 給MathJax元素添加has-jax class --> <script type="text/x-mathjax-config"> MathJax.Hub.Queue(function() { var all = MathJax.Hub.getAllJax(), i; for(i=0; i < all.length; i += 1) { all[i].SourceElement().parentNode.className += ' has-jax'; } }); </script> <!-- 通過連接CDN加載MathJax的js代碼 --> <script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML"> </script>

只在有公式的頁面才加載MathJax

有公式才加載MathJax,這點比較重要,沒有公式仍然加載js渲染公式,會影響頁面加載速度。

在所有有公式的文章的front-matter中增加一項配置 mathjax: true,例如:

---
title: 結構化機器學習項目
tags:
  - 機器學習
categories:
  - 機器學習
date: 2017-10-9 22:22:00
toc: false
mathjax: true
---

然後在themes/free2mind/layout/_partial/footer.ejs 中通過此配置變量決定是否加載mathjax.ejs :

<!-- 根據頁面mathjax變量決定是否加載MathJax數學公式js -->
<% if (page.mathjax){ %>
<%- partial('mathjax') %>
<% } %>

解決MarkDown與MathJax渲染沖突

添加MathJax後寫幾個公式發現渲染出了很多問題,原因是Hexo默認先使用hexo-renderer-marked引擎渲染MarkDown,然後再交給MathJax渲染。hexo-renderer-marked會把一些特殊的markdown符號轉換為相應的html標簽,比如在markdown語法中,下劃線 _ 代表斜體,會被轉化為< em>標簽,\\也會被轉義成一個\。而類Latex格式書寫的數學公式下劃線 _ 表示角標,\\表示公式換行,有特殊的含義,所以MathJax引擎在渲染數學公式的時候就會出錯。

解決方法有人提出更換Hexo的MarkDown渲染引擎,用hexo-renderer-kramed 替換默認的hexo-renderer-marked引擎,但我看了下hexo-renderer-kramed的文檔說明,如果用這個引擎的話,要改變我的MarkDown書寫習慣,還是不用了,並且換了這個引擎還是沒有完全解決問題。

最終解決方法是參考一篇博文中修改hexo-renderer-marked渲染引擎的js腳本,去掉對 _ 和\\的轉義。
Hexo默認的MarkDown渲染引擎hexo-renderer-marked會調用marked模塊的marked.js腳本進行最終的解釋,這個腳本在Hexo安裝後的node_modules\marked\lib\目錄中。
有兩點修改:

針對下劃線的問題,取消_作為斜體轉義,因為marked.js中*也是斜體的意思,所以取消掉_的轉義並不影響使用markdown,我平時一般不用斜體,就是用也更習慣用*作為斜體標記。
針對marked.js與Mathjax對於個別字符二次轉義的問題,我們只要不讓marked.js去轉義\\,\{,\}在MathJax中有特殊用途的字符就行了。
編輯node_modules\marked\lib\marked.js 腳本,

【第一步】
將451行的escape: /^\\([\\`*{}\[\]()# +\-.!_>])/,
替換為
escape: /^\\([`*\[\]()# +\-.!_>])/,
這一步取消了對\\,\{,\}的轉義(escape)

【第二步】
將459行的em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
替換為
em:/^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
這一步取消了對斜體標記_的轉義

這樣帶來一個問題就是,以後每次更換電腦,在新電腦上安裝完Hexo環境後,都要手動修改marked.js文件。


MathJax公式書寫

公式書寫依然按照MarkDown語法來,基本上也和LaTeX相同,單\$符引住的是行內公式,雙\$符引住的是行間公式。

  • MathJax公式書寫參考
    http://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference

MathJax行內公式

含下劃線_的公式: \(x_mu\)

$x_mu$

希臘字符: \(\sigma\)

$\sigma$

行內公式: \(y=ax+b\)

$y=ax+b$

行內公式: \(\cos 2\theta = \cos^2 \theta - \sin^2 \theta = 2 \cos^2 \theta\)

$\cos 2\theta = \cos^2 \theta - \sin^2 \theta = 2 \cos^2 \theta$

行內公式: \(M(\beta^{\ast}(D),D) \subseteq C\)

$M(\beta^{\ast}(D),D) \subseteq C$

MathJax行間公式

行間公式:

$$ \sum_{i=0}^n i^2 = \frac{(n^2+n)(2n+1)}{6} $$

\[ \sum_{i=0}^n i^2 = \frac{(n^2+n)(2n+1)}{6} \]

行間公式:

$$ x = \dfrac{-b \pm \sqrt{b^2 - 4ac}}{2a} $$

\[ x = \dfrac{-b \pm \sqrt{b^2 - 4ac}}{2a} \]

MathJax大括號右多行賦值

雙\\公式內換行,cases實現大括號右多行賦值,&用來對齊:

$$
f(n) =
\begin{cases}
n/2,  & \text{if $n$ is even} \\
3n+1, & \text{if $n$ is odd}
\end{cases}
$$

\[ f(n) = \begin{cases} n/2, & \text{if $n$ is even} \3n+1, & \text{if $n$ is odd} \end{cases} \]

MathJax多行公式對齊

比如多行公式推導中常用的等號對齊
begin{split} 表示開始多行公式,end{split}表示結束;公式中用\\表示回車到下一行,&表示對齊的位置。

$$
\begin{equation}
\begin{split}
\frac{\partial^2 f}{\partial{x^2}} &= \frac{\partial(\Delta_x f(i,j))}{\partial x} = \frac{\partial(f(i+1,j)-f(i,j))}{\partial x} \\
&= \frac{\partial f(i+1,j)}{\partial x} - \frac{\partial f(i,j)}{\partial x} \\
&= f(i+2,j) -2f(f+1,j) + f(i,j)
\end{split}
\nonumber
\end{equation}
$$

\[ \begin{equation} \begin{split} \frac{\partial^2 f}{\partial{x^2}} &= \frac{\partial(\Delta_x f(i,j))}{\partial x} = \frac{\partial(f(i+1,j)-f(i,j))}{\partial x} \&= \frac{\partial f(i+1,j)}{\partial x} - \frac{\partial f(i,j)}{\partial x} \&= f(i+2,j) -2f(f+1,j) + f(i,j) \end{split} \nonumber \end{equation} \]

MathJax公式自動編號

要想MathJax支持公式編號,需添加AMS支持,在腳本中添加如下MathJax配置項:

<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  TeX: { equationNumbers: { autoNumber: "AMS" } }
});
</script>

我上面mathjax.ejs腳本中已加入公式編號的配置。
書寫時只要使用begin{equation}環境就會自動編號:

$$
\begin{equation}
\end{equation}
$$

註意此時會自動將文檔內的所有begin{equation}公式連續編號,例如:

$$
\begin{equation}
\sum_{i=0}^n F_i \cdot \phi (H, p_i) - \sum_{i=1}^n a_i \cdot ( \tilde{x_i}, \tilde{y_i}) + b_i \cdot ( \tilde{x_i}^2 , \tilde{y_i}^2 )
\end{equation}
$$
$$
\begin{equation}
\beta^*(D) = \mathop{argmin} \limits_{\beta} \lambda {||\beta||}^2 + \sum_{i=1}^n max(0, 1 - y_i f_{\beta}(x_i)) 
\end{equation}
$$

\[ \begin{equation} \sum_{i=0}^n F_i \cdot \phi (H, p_i) - \sum_{i=1}^n a_i \cdot ( \tilde{x_i}, \tilde{y_i}) + b_i \cdot ( \tilde{x_i}^2 , \tilde{y_i}^2 ) \end{equation} \]
\[ \begin{equation} \beta^*(D) = \mathop{argmin} \limits_{\beta} \lambda {||\beta||}^2 + \sum_{i=1}^n max(0, 1 - y_i f_{\beta}(x_i)) \end{equation} \]

禁止自動編號

在end{equation}前加\nonumber可禁止對此公式自動編號,例如:

$$
\begin{equation}
\sum_{i=0}^n F_i \cdot \phi (H, p_i) - \sum_{i=1}^n a_i \cdot ( \tilde{x_i}, \tilde{y_i}) + b_i \cdot ( \tilde{x_i}^2 , \tilde{y_i}^2 )
\nonumber
\end{equation}
$$
$$
\begin{equation}
\beta^*(D) = \mathop{argmin} \limits_{\beta} \lambda {||\beta||}^2 + \sum_{i=1}^n max(0, 1 - y_i f_{\beta}(x_i)) 
\end{equation}
$$

\[ \begin{equation} \sum_{i=0}^n F_i \cdot \phi (H, p_i) - \sum_{i=1}^n a_i \cdot ( \tilde{x_i}, \tilde{y_i}) + b_i \cdot ( \tilde{x_i}^2 , \tilde{y_i}^2 ) \nonumber \end{equation} \]
\[ \begin{equation} \beta^*(D) = \mathop{argmin} \limits_{\beta} \lambda {||\beta||}^2 + \sum_{i=1}^n max(0, 1 - y_i f_{\beta}(x_i)) \end{equation} \]

MathJax公式手動編號

可以在公式書寫時使用\tag{手動編號}添加手動編號,例如:

$$
\begin{equation}
\sum_{i=0}^n F_i \cdot \phi (H, p_i) - \sum_{i=1}^n a_i \cdot ( \tilde{x_i}, \tilde{y_i}) + b_i \cdot ( \tilde{x_i}^2 , \tilde{y_i}^2 ) \tag{1.2.3}
\end{equation}
$$

\[ \begin{equation} \sum_{i=0}^n F_i \cdot \phi (H, p_i) - \sum_{i=1}^n a_i \cdot ( \tilde{x_i}, \tilde{y_i}) + b_i \cdot ( \tilde{x_i}^2 , \tilde{y_i}^2 ) \tag{1.2.3} \end{equation} \]

不加\begin{equation}, \end{equation}也可以,例如:

$$
\beta^*(D) = \mathop{argmin} \limits_{\beta} \lambda {||\beta||}^2 + \sum_{i=1}^n max(0, 1 - y_i f_{\beta}(x_i)) \tag{我的公式3}
$$

\[ \beta^*(D) = \mathop{argmin} \limits_{\beta} \lambda {||\beta||}^2 + \sum_{i=1}^n max(0, 1 - y_i f_{\beta}(x_i)) \tag{我的公式3} \]

行內公式加\tag{}後會自動成為行間公式,例如:

$z = (p_0, ..... , p_n) \tag{公式21} $

$z = (p_0, ..... , p_n) \tag{公式21} $

又如:

$ s = r cos(a+b) = r cos(a) cos(b) - r sin(a) sin(b) \tag{1.1} $
$ t = r sin(a+b) = r sin(a) cos(b) - r cos(a) sin(b) \tag{1.2} $

$ s = r cos(a+b) = r cos(a) cos(b) - r sin(a) sin(b) \tag{1.1} $
$ t = r sin(a+b) = r sin(a) cos(b) - r cos(a) sin(b) \tag{1.2} $

  • 參考Automatic Equation Numbering
    http://docs.mathjax.org/en/latest/tex.html#automatic-equation-numbering

將下標放到正下方

1、如果是數學符號,那麽直接用\limits命令放在正下方,如Max函數下面的取值範圍,需要放在Max的正下方。可以如下實現:

$ \max \limits_{a<x<b}\{f(x)\} $

$ \max \limits_{a<x<b}{f(x)} $

2、若是普通符號,那麽要用\mathop先轉成數學符號再用\limits,如

$ \mathop{a}\limits_{i=1} $

$ \mathop{a}\limits_{i=1} $

MathJax矩陣輸入

無括號矩陣:

$$
\begin{matrix}
1 & x & x^2 \\
1 & y & y^2 \\
1 & z & z^2 \\
\end{matrix}
$$

\[ \begin{matrix} 1 & x & x^2 \1 & y & y^2 \1 & z & z^2 \\end{matrix} \]

矩陣運算:

$$
\left(
    \begin{array}{c}
      s \\
      t
    \end{array}
\right)
=
\left(
    \begin{array}{cc}
      cos(b) & -sin(b) \\
      sin(b) & cos(b)
    \end{array}
\right)
\left(
    \begin{array}{c}
      x \\
      y
    \end{array}
\right)
$$

\[ \left( \begin{array}{c} s \ t \end{array} \right) = \left( \begin{array}{cc} cos(b) & -sin(b) \ sin(b) & cos(b) \end{array} \right) \left( \begin{array}{c} x \ y \end{array} \right) \]

有括號有豎線矩陣:

$$
\left[
    \begin{array}{cc|c}
      1&2&3\\
      4&5&6
    \end{array}
\right] 
$$

\[ \left[ \begin{array}{cc|c} 1&2&3\ 4&5&6 \end{array} \right] \]

行內小矩陣:

$\bigl( \begin{smallmatrix} a & b \\ c & d \end{smallmatrix} \bigr)$

這是一個行內小矩陣\(\bigl( \begin{smallmatrix} a & b \\ c & d \end{smallmatrix} \bigr)\),直接嵌入行內。

在Hexo中渲染MathJax數學公式