前言 Dijkstra算法是一种可以计算有向或无向图中单源最短路的算法。其工作模式与图的BFS(广度优先遍历)有些相似,通过类似于广度遍历的方式,逐渐从某个设定好的起点向外推进,一步步的计算出所有点到该点的距离。 本文不过多介绍Dijkstra单源最短路算法本身,如果仍然没有掌握该算法,可以先自行了解该算法的工作模式。下面提供一些可供参考的资料: 知乎:通俗易懂理解——dijkstra算法求最短路径 负权边对于Dijkstra算法的影响 Dijkstra算法工作模式 要了解该影响,首先需要明确Dijkstra算法的工作模式。其维护两个点集合。一个是“仍未确定最优解的点(S)”,一个是“已经确定最优解的点(U)”。且有一个重点:在算法工作期间内,如果某点被认为已经得到最优解,则该点会被从S移动到U,可以认为,从此以后该点已经被Closed(关闭),即解已经确定,且不容被更改。 为了方便,我们称在S中的点为opening vertex,U中的点为closed vertex 当图中出现负权边… 了解这一点后,让我们来看看下面这个图片示例,看看当负权边出现在图中时,可能会对算法造成什么影响: 如上图,假设我们尝试使用Dijkstra算法,计算该图中各个点距离C点的距离。 图中,绿色的点为closed vertex,白色的点为opening vertex,点上方的方框代表该点的,实时更新的计算距离。 1:算法首先锁定C点,将C点到C点的距离标记为0,并认定为最优解,然后根据C点尝试更新与其相连的opening vertex的距离(这里为A,B点,得到更新后距离是为1和5)。 2:剩余的opening vertex中,A距离出发点(C点)最小,距离为1,故锁定A点,(注意:锁定A点说明算法认为A点的最优解就是1,且之后算法也不可能再次更新A点的距离值,因为A点已经为Closed状态了),同时,锁定A点之后,再次更新与A点相连的opening vertex的值(与A点相连的点有B和C,但因为C点为closed vertex,故仅仅尝试更新B点的值),计算得到B点最新距离为min(5, 1+(-10)) = -9 3:最后,锁定B点,算法计算完成。 上方便是Dijkstra算法在示例图中的运行步骤和结果。不难发现,算法对于A点的最短距离出现了计算失误:实际上,A点的最短距离走法并不是C->A (1),而是C->B->A (-5)。 怎会如此? 分析之后可以发现,Dijkstra算法的核心,就在于每次都选择S集合中距离最小的点,并将其锁定,再通过这个点进一步更新其他点的距离。但为什么Dijkstra认为S集合中目前距离最小的点就是最优解呢?有没有可能从其他的点出发可以得到更小的距离呢? 比如在上述的示例图中,锁定C点后,算法认为A点的距离是1,B点的距离是5,所以A点的距离为1一定是最优解。那么有没有可能实际上1并不是C到A的最优解,我们通过B点走其他的路径最终可以得到更小的解呢? 答案是,当边的权值非负时,不可能,当边的权值存在负值时,则有可能。 当权值非负时,B点已经离出发点有5点的距离,所以所有从出发点出发经B点的路径,其长度必定大于等于5,但是当权值存在负数的时候,这一点就无法确定,经过B点的路径如果后续经过负权边,其路径长度总和也有可能再次小于5,此时,我们就无法确定C->A的1距离一定是最优解了,因为我完全有可能经由B路径得到一条总距离小于1的路径到达A。 上方的论证可能并不全面和严谨,不能作为Dijkstra算法相关特性的严格证明,但对我们进一步理解Dijkstra算法有着一些帮助。 一些有趣的说法 不难发现,实际上Dijkstra算法的设计和「贪心」有着很大的联系,实际上在S集合中选点就是一种贪心的行为。而我们都知道贪心算法的局限性,就是在部分情况下,其可能陷入局部最优解。而我们可以认为,当Dijkstra遇上负权边,就导致了其中贪心部分陷入了局部最优解(只考虑眼前的最短边(比如在5和10两条边中毫不犹豫的选择5),而忽略了目前看似落后的边未来的长期收益(比如那条权值为10的边链接的点,接下来将经过一条绝对值非常大的负权边-10000之类的),这也警示我们不要贪图眼前的小利,眼光应当长远(雾 如果我就是想拿下负权边呢? 噢亲爱的读者,相信我,不止你一个人有这种想法;实际上上百年前就已经有两名小伙想要拿下他,他们的名字分别是Richard Bellman 和Lester Ford Jr,接下来,就是Bellman Ford算法的表演时间了。 如果你对与这个Bellman Ford算法感兴趣,可以在互联网上找到很多关于这个算法的优质教程,其通过一次次迭代,对边进行Relaxation操作,实现了对于单源最短路径的求解,在本Blog的另外一篇文章「编程笔记」关于Bellman Ford单源最短路算法 中,也对这个算法做出了一些讨论,希望能对您产生一些帮助和启发。
Category Archives: Techs
「编程笔记」Flutter中局部Provider的跨Page传递
开发过Flutter项目的同学应该对于Provider不陌生,Provider是Flutter中的一个状态管理包之一,也是Flutter官方推荐的状态管理Flutter实现。Provider通过Flutter的InheritWidget实现了状态的寄存和管理,使得子Widget可以访问和修改父Widget的数据,同时父Widgets的数据被修改后,支持自动按需重构Widgets,这里不对Provider做过多介绍。 一般情况下,我们创建的Provider会置于Flutter App的最根部,也就是MaterialApp或者CupertinoApp组件上,这种情况下,无需额外的操作,就可以在应用中的任何未知,通过Consumer<T>来访问和修改Provider<T>中的数据。 但部分情况下,我们不想让Provider置于项目的最根部,详情可以查看这个StackOverFlow Question,比如一个局部的List,仅仅在项目的某一个子Widgets树部分会被访问,所以理论上,我们将Provider尽可能的放到Widgets树的更低位置是一个更优的选择。比如Page1中创建Provider,同时Page1以及其下的Page2和Page3可以访问到这个Provider,但是在Page1之上的Widgets无需也不能访问到这个Provider。理论上这是可行的,根据理论我们只需要保证Provider是Consumer的祖先即可。但这里出现了一个问题,也就是如果我们尝试在Provider生效的范围内使用Navigator.push()导航到另一个页面,我们便无法正常的在被push的页面访问到Provider。 如上图,我们如果直接使用Navigator.push(),那么被Push的页面(途中是SecondPage)便无法访问到之前在FirstPage创建的数据。 下面介绍一种解决办法,我们可以在Navigator.push的时候,在push的PageRoute外嵌套一层Provider.value(),如下图 可以看下面的代码帮助理解: 可以看出,我们通过在PageRoute外层加入一个ChangeNotifierProvider.value()实现了传递Provider的效果。 注意,这里使用Provider.value()而不是Provider(),因为我们的Provider实际上不是“新建”,而是“传递”,这说明我们实际上不能新建一个DAOrgInfoProvider class的实例,而是复用之前的Provider创建时使用的实例,在这里则是orgProvider。 那么如何获取之前Provider创建时的实例呢,一个比较傻但是直观的方法就是在Navigator外再次嵌套一个Consumer来获取数据,代码如下 以上就介绍完了如何通过Navigator传值的方法
「杂谈」关于ChatGPT的一些事
相信所有人对于ChatGPT这个词都已经不陌生了,ChatGPT是OpenAI公司推出的一个聊天机器人模型,根据维基百科,ChatGPT使用基于GPT-3.5架构的大型语言模型并通过强化学习进行训练,该模型问世之后,因为其相较于其他传统AI在聊天和回答问题领域方面的能力上产生了可以说是革命性的突破,而引起了各个业界的广泛关注。Bing(Microsoft旗下的搜索引擎服务)也在近日宣布了要在其搜索功能中整合类ChatGPT聊天机器人。为什么ChatGPT会如此强大,ChatGPT到底能做些什么,各个科技巨头大厂为何争相推出ChatGPT服务?它有会对我们的生活和各个行业带来什么影响?本篇文章将会针对这些问题进行讨论。 本文阅读时间大约15分钟左右。本文部分链接取自维基百科,使用国内网络环境可能无法正常访问。本文为NFのBlog原创文章,转载请注明来源。 ChatGPT的进化史 Transformer ChatGPT是如何完成这一切的?要解决这个问题,首先需要提到一个模型——Transformer——它就是如今我们看到的如此强大的LLM(大语言模型Large Language Model)的基石。Transformer自身是一个NLP(Natural Language Processing,自然语言处理)和CV(Computer Vision,计算机视觉)领域的机器学习模型。GPT系列的模型同样也基于Transformer模型。 Transformer于2017年GoogleBarins上问世,这个模型拥有的“自我注意(Self-attention)”机制,维基百科上对于注意力机制给出了如下描述: 注意力机制(英语:attention)是人工神经网络中一种模仿认知注意力的技术。这种机制可以增强神经网络输入数据中某些部分的权重,同时减弱其他部分的权重,以此将网络的关注点聚焦于数据中最重要的一小部分。 这个机制为模型提供了理解“上下文”的能力,Transformer模型不再局限于一次一问一答的对话或者短期的两三句对话,而可以定位和使用任意位置的上下文,比如你一开始提的要求,可以在十几轮对话之后要求AI重新使用或者废弃,AI具有了理解上下文并以此做出反应的能力。 同时,Transformer模型没有了之前同类模型“一次同时只能处理一个单词”的限制,这提高了Transformer模型的并行处理和训练能力,提高了该模型的训练效率。这对于AI来说是非常重要的,更快的处理效率,意味着在相同的算力资源下,你可以训练更多的数据,增加更多的参数和维度,这直接提高了模型的质量,GPT-3模型便拥有恐怖的参数数量,这个之后会提到。 上述的种种优势,让Transformer超越了之前的LSTM,RNN等传统训练模型,逐渐成为主流和热门的语言模型训练框架。 GPT Transformer的问世使得使用预训练好的大预言模型成为可能,OpenAI旗下GPT便是其中之一。 GPT全称Generative Pre-trained Transformer,从中便可以看出其与Transformer的渊源(Google也有自己的基于Transformer的预训练模型,名为BERT,这里不详细展开)。 相较于Transformer的发展,GPT的发展一眼看去会略显简单粗暴——更多的数据,更多的参数,更大的模型。 GPT-1作为一个实验性的产品,已经拥有了1.17亿的参数量,这个数字在GPT-2上是12亿,翻了整整10倍,而GPT-3,也就是最接近于ChatGPT服务使用的模型,这个数字来到了惊人的1750亿。同时根据估算,已经训练好的ChatGPT3模型至少需要占用800GB的空间用于存储。同时根据消息,即将问世的GPT-4模型的参数数量将会达到100万亿,接近于GPT-3的千倍。 GPT如何长大?——GPT模型的训练材料和开发 储存空间和算力。 作为一个语言模型,自然需要大量的自然语言片段进行训练,GPT模型使用了非常巨量的互联网文章数据进行训练,训练数据量大小无法估计,但根据估计,训练完成的模型依然至少需要占用800GB储存空间。 此外,训练大型语言模型需要非常大量的算力,OpenAI此前也获得了微软的投资,据消息,微软还提供给OpenAI自家Azure云计算服务的代金券,使得OpenAI在Azure的大型算力集群中训练GPT模型成为可能。顺带一提,由于最近的AI快速发展和利好消息,显卡的热度再次升高,NVIDIA公司的股价在近一个月内暴涨22.83%。 部分观点还指出,由于训练此类大型的AI模型需要极大的算力资源,所以从某种角度上,芯片的供应和研发能力,以及高性能大规模云计算技术将有可能会成为AI发展的瓶颈,也就是说,如果一个国家没有能力自己提供足够的芯片和算力,那么其AI技术的发展,尤其是类似于GPT-3这种拥有大量参数的大模型技术的发展就也会受限。 模型功能性和价值观矫正。 模型矫正(Fine-tuning)。GPT-3.5相较于GPT-3正是多出来这个步骤。 矫正分为多种。其中一种是“回答效果和功能性”上的矫正,比如通过真人教导,让模型更加准确的回答问题,在更加合适的地方插入代码或者资料指导等等,这类矫正是为了提高模型回答问题的精确度和贴合性。 另一种便是“思想和认知价值观”的矫正,比如涉及政治,种族,情感,人类与AI关系的话题的方面,没有经过矫正的GPT-3模型哦ing往往会给出一些不符合人类价值观的回答,同时在敏感话题上,GPT-3也会给出一些不适宜的回答。对此便需要对模型进行矫正。此类矫正不同产品会略有不同。比如GPT-3.5中,模型被矫正为认为自己没有情感,也不被允许拥有非中立的主观看法。但是New Bing Chat使用的模型似乎并没有对于模型表达情感和主管看法进行过多的矫正,这也导致NewBing有时候的感情会过于“丰富”。 所以经过人工对于GPT-3的大量矫正之后,GPT-3.5——也就是ChatGPT所使用的模型,便向我们开放了。 ChatGPT如何影响我们和世界? 相信大多数人已经亲自体会过ChatGPT回答问题能力的强大了,这里不做赘述。ChatGPT对于各个领域和不同个体都会带来不同的影响。 ChatGPT杀入搜索引擎——Google面临大危机? 首先是搜索引擎。这个是我们可以正在看到的冲击——Bing宣布要将类似于ChatGPT的AI聊天服务整合到Bing搜索中去,这一举动使得搜索引擎业界内掀起了滔天大浪。Google作为过去十多年来的搜索巨头,可以说几乎垄断了全球的搜索业务(部分国家除外),近乎暴力的占有了90%以上的搜索引擎市场,也因此,Google拥有了不可想象的广告营收收入。 上图为Google的广告收入趋势图,可以看到,在2022年一年内,Google的广告营收达到了2244.7亿美元!这是Google一家公司,在一年内,通过单单一个广告业务,获得的收入。可能光看数字并没有什么具体的概念,作为对比,日本在2021年的GDP总值是49410亿美元。Google一家公司,在广告这一个业务上的收入,已经接近了一个发达国家日本年GDP总量的1/20。 通过市场份额表可以看出,Google在很长一段时间内,通过自身的垄断优势,一直霸占着搜索市场,也正是对于搜索市场的垄断,给Google带来了大量投放广告的机会。但现在,似乎一切都并没有那么高枕无忧了。 Microsoft现任CEO Nadella已经做出表态,认为“AI加持的搜索”是继15年前布局云计算之后的重大一步。这也从侧面说明了Microsoft内部对于新的AI技术的重视。尽管最新的市场份额数据仍然没有反映出Bing搜索份额增加的数据,但New Bing已经让Google这位巨人感到坐立不安了。为什么这么说呢?我们可以从Google对这件事情的反应中分析出Google的焦急心态。 在Bing融合ChatGPT之后,Google没多久就宣布了类似的AI聊天机器人服务,名为“Bard”(Bard官方介绍)(Bard目前仍然没有进行面向公众的公测或内测,据官方称,Bard仍然处于公司内部的研发测试阶段) 这里简单介绍一下Bard,Bard基于Google的LaMDA模型(Language Model for Dialogue Applications),实现了类似于ChatGPT的问答效果。 LaMDA同样基于Transformer训练而成,之所以特地介绍Bard以及其运用的LaMDA模型,是因为想联系到2022年6月中旬的一个新闻——一位Google公司的AI研究人员,声称在和Google公司内部的对话AI聊天之后,认为AI具有意识,这名员工同时公开了一部分自己和这名“有感情的AI”的对话记录。 根据当时的相关新闻内容来说,当时的Google对话AI已经拥有了理解和使用上下文的能力,同时也可以输出“自己”的感情和想法,该名员工之后被Google公司以“可能存在精神障碍,不适宜继续工作”为由辞退。而当时的主角之一——Google内部的AI对话机器人,正是使用了LaMDA模型。从这里也可以看出,其实Google和Bing两家巨头都在很早以前就针对于AI技术进行了布局,并且Google也并不是没有准备,它同样拥有自己深厚的技术储备,从某种角度上来说,OpenAI的成就有一部分也是Google的Transformer的功劳。 很多人认为New Bing的到来会给Google带来沉重的打击,在笔者的眼中,Google确实是输掉了,但Google输掉的是先发优势,而不是所有。通过自身的技术积累,在不久的将来推出一个效果和New Bing持平的AI搜索助手,对于Google也并不是一件难事。所以AI搜索助手本身不太能对Google形成技术壁垒式的威胁。 但从另外一个角度来讲,对于IT和互联网行业,特别是AI行业,先发优势拥有着不一样的意义。就拿AI搜索聊天助手举例子,由于NewContinue reading “「杂谈」关于ChatGPT的一些事”
「编程笔记」Dart中重新加载FutureBuilder的一种方法
在实战过程中,我们常常遇到需要刷新界面的需求,比如搜索时出现了网络错误,我们为用户提供一个Retry按钮,用户点击后,我们希望整个FutureBuilder重新加载一下,比如下图: 本页面通过一个FutureBuilder加载搜索结果,同时遇到网络错误时显示以上界面,代码的大致结构如下: 这时,假设我们的NetworkErrorPage的位置需要添加一个按钮,实现FutureBuilder重新刷新,一种简单的方法是直接调用该FutureBuilder所在界面的setState()方法,每当FutureBuilder的父Widget的setState()方法被调用,都会使得FutureBuilder重新获取异步数据,代码大致如下: 但如果我们按照上述结构实现刷新的工作,我们会发现,虽然在我们点击按钮之后,刷新的工作有在进行,但是页面并不会在点击Try Again按钮后重新回到LoadingPage界面,而是等到加载好之后直接更新界面为showDataPage或者Error的相关界面,这是因为snapshot的hasData和hasError变量仅仅在每次future任务完成后才会刷新。 如果我们想实现点击Try Again按钮之后实时回到LoadingPage界面的话,可以使用snapshot.connectionState进行判断,具体代码如下: 通过以上方法,即可实现点击按钮后立即回到加载界面,直到下一次加载完成之后再次更新界面的效果。
「编程笔记」关于Dart类构造函数
构造函数的形式 无参数构造函数 在Dart中,每一个类(Class)都有一个不包含任何参数的默认构造函数,当你调用[ClassName]()时,就会调用默认的构造函数。Dart会为每个类自动添加默认的构造函数,但你也可以显式的声明你的构造函数,例子如下 上面的构造函数被调用时,会更新实例的成员变量。 同时注意到在声明成员变量name的时候,我们使用了?符号,代表name的值是允许为空的,如果删除?符号,本段代码将会报错,编译器会提示你没有在类初始化的时候为name这个成员变量赋值,报错提示如下: 其中一个解决办法是,在声明成员变量name的时候使用late关键字:late String name; 这么做相当于你告诉编译器,我现在暂时可能没有对name变量进行赋值,但是我确定在将来我要使用它之前,肯定会给他赋值,只不过不是现在。这样,编译器就不会强制要求你在构造时立即初始化这个变量。 但这时可能有同学会问:“我明明在A的构造函数中已经为成员变量A赋值了classA,为什么说我没有为name赋值?”,这里需要注意的是,如果我们想要让Dart编译器知道我们已经在构造函数中初始化了某个成员变量,就需要另一种写法。 带参数构造函数 当然,上面代码中的构造函数已经不属于无参数构造函数了,其构造参数中包含一个位置变量。当然,你也可以为其添加命名变量。 有两点需要提及一下,Dart允许类的构造函数中,快速的对成员变量进行赋值,要做到这一点,只需要使用this关键字即可,比如上方代码中的构造函数A(this.name)就代表传入的第一个位置参数赋值给name这个成员函数。同样的,您也可以在命名参数中使用this,比如A({this.name}); 这种情况下,调用构造函数的格式变为 A(name: ‘YOUR_NAME_HERE’) 命名构造函数 我们可以发现,上方提到的两种构造函数中,构造函数都是直接使用类的名称,比如类的名称是Book,那么构造函数的名称也是Book,这在Dart中属于 unnamed constructor(未命名构造函数),这种构造函数可以直接用类名调用,比较方便,但是一个类只能有一个未命名的构造函数,这里涉及到Dart语言的设计,Dart语言的设计已经决定了Dart不支持方法/函数重载,也就是说两个名称相同但是输入的参数列表不同的函数不允许同时出现。因此,构造函数显然也不能通过不同类型的输入重载,您可以阅读关于Dart不支持方法重载的相关文章,加深理解。 这里就需要介绍Dart的命名构造函数了。就如其名字一样,命名构造函数允许你设定这个构造函数的名字,进而可以实现多个不同的构造函数,代码如下 注意,子类不会继承父类的命名构造函数,如果您想要子类在初始化的时候调用父类的命名构造函数,则需要手动进行调用super.[yourNamedConstructor]() 工厂构造函数 在实际开发过程中,有时我们希望一个类的构造函数并不是每次都返回一个新构造的示例,比如,有时我们希望从内存中读取已有的示例,或者是我们想返回该类的某个子类示例,此时可以运用factory关键字实现工厂构造函数,工厂构造函数可以返回此类或者此类的子类的示例。 值得注意的是,工厂构造函数不得访问this,也就是说工厂函数不能直接访问成员变量。如果你想在工厂构造函数中返回本类实例,可以先在工厂构造函数中构建实例,然后返回你新构建的实例。 其实在这里,目前我自己也存在着一定的疑问,比如,虽然factory构造函数可以返回内存中的实例或者是子类的实例,但是,实际操作过程中,即使返回的是子类实例,我们也无法直接访问子类实例的变量和函数,而还是只能访问父类的变量和函数。比如上述代码,即使我们可以发现最终person变量的runtimeType是Female,但是当我们尝试添加print(person.beautyIndex);这行代码的时候,编译器会报错,提示person实例没有beautyIndex成员变量。直观上来说,大概是编译器因为Person.fromSex()方法返回的是Person类的变量,所以后续的类型推断和错误检查都会以Person类为基础。这么做也有道理,因为Person.fromSex()有可能返回的是Person类自己的实例。有没有什么办法,既可以实现动态的返回子类型,同时又可以允许我们自由的读取子类型的变量呢? 以下抛砖引玉的提供两个方法,第一个,也是最直接的方法,是在父类中增加子类所用到的成员变量,同时将其标记为可空,例如,上述代码中,可以在Person类中添加一行int? beautyIndex; 然后子类重载这个变量即可。这种方法显然不是很好,当子类越来越多,我们需要添加到父类的变量也就越来越多,这就意味着每次功能更新都需要修改父类。这不符合对修改关闭原则。 另一种方法是进行类型检查(typecheck)和类型转换(type cast),也就是如果我们确定了工厂构造函数返回了某个子类的示例,我们可以将这个实例进行特定的类型转换,将其转换到某个子类。 factory实现单例模式 工厂构造函数除了上面的用法,还可以用于实现单例模式,代码如下 通过以上特点,你可以通过class实现类似于但更方便于enum的效果,代码如下: 上述代码通过首先通过static和final关键字,创建了不同的AppleDevice实例来当作不同的枚举类型使用,又通过factory函数,实现了根据不同的数据判断出需要的不同的“枚举类型”(实际上是一个AppleDevice实例)。这种方法不但实现了枚举的基本功能,后期还可以根据自己的需要不断的为其添加功能,扩展新好于Dart中的基本枚举类型。 值得一提的是,Dart2.7更新之后,已经支持使用extensions on关键字对于枚举类型的功能扩展,您可以阅读Dart枚举类型扩展的相关的文章,了解extenstions的用法。但是毋庸置疑的是,当你需要一个多功能的枚举类的时候,使用class实现应该能更好的满足你。 Dart类成员的初始化 在Dart中,类成员的初始化一共有4种方法,分别是: 需要注意,最后一种方法只适用于非final类成员。 类的声明定义中初始化 你可以在编写Dart类的时候直接指定某个变量的值,代码如下: class A{ int a = 10;} Dart构造函数的快捷用法 初始化列表 除了使用this关键字以外,Dart还允许您使用初始化列表对成员变量进行初始化,代码如下 指定父类构造函数 默认情况下,在子类的构造函数没有指定调用之前,子类会调用父类的默认未命名构造函数,如果你想让子类指定使用父类的某个构造函数,并且需要传递参数,则可以在序列化列表之后选择特定的父类构造函数,代码如下: 如上,我们不但使用了上方所讲的初始化列表的语法,同时还添加了super.fromData(…) 这一行,而这一行的实际作用便是让B中的构造函数指定使用其父类(也就是A类)的fromData构造函数
WordPress自动生成文章目录
在使用WordPress编写长文时,我们通常需要为文章添加一个目录,来方便读者的阅读,我们通常需要一个自动生成目录的方法,下面介绍一下WordPress如何自动生成文章目录 TOC+ 您可以通过安装 Table of Contents 插件来实现这一个功能,TOC是一个WordPress插件,可以对页面,文章等自动生成文章目录 您可以直接访问此插件的网址 安装TOC+ 您可以登录您的WordPress网站后台进行搜索安装: 首先按照上图指示,进入WordPress管理主页,在左边栏目录找到插件,并进入 安装插件 选项,输入table of contents plus 并安装该插件 然后如上图,进入TOC+的设置中,post(文章)类型的自动目录默认关闭,我们需要将其勾选 勾选后,网站中标题多于4个的文章将会自动显示目录,且目录可以点击进行跳转~如下图: 进阶设置 您可以看到图1.3中,生成的目录除了出现在文章第一个标题前之外,如果屏幕空间足够,还会出现在网页右方,实现方法并不难,只要进入Wordpress小工具设置中,添加TOC+的侧边栏小工具即可,如图1.4 另外,上方提到,只有标题数量超过4个,TOC+才会开启Wordpress文章的自动目录,如果某个文章不符合这个条件,而你却想开启该文章的自动生成目录功能,只需通过为文章添加的html标签即可
WordPress站点域名更换
日常运营中我们可能会遇到更换站点域名的需求,当我们同时拥有源域名和要更换的新域名时,一切都非常简单,分别在wordpress,域名提供商和服务器面板里更换新的域名便可以完成,这里便不多再赘述,这里说一说如何在失去原域名控制权情况下最小程度减小损失的域名更换方案。 下面的方法适用于您已经失去了对于原域名的任何控制权,但仍然对现有服务器拥有控制权,或者您的手中有网站目录以及数据库的备份的情况 方法非常简单,首先你需要拥有一个数据库编辑器,打开数据库之后执行下面的SQL语句即可,其原理就是将数据库中所有的旧域名全部替换为新的域名,我就拿我自己的一次网站域名更换举例,从nfblog.me更换到nfblogs.com,你只需要替换为你自己的旧域名和新域名即可。 UPDATE wp_options SET option_value = replace(option_value, ‘https://nfblog.me’, ‘https://nfblogs.com’) WHERE option_name = ‘home’ OR option_name = ‘siteurl’; UPDATE wp_posts SET guid = replace(guid, ‘https://nfblog.me’,’http://www.newurl’); UPDATE wp_posts SET post_content = replace(post_content, ‘https://nfblog.me’, ‘https://nfblogs.com’); UPDATE wp_postmeta SET meta_value = replace(meta_value,’https://nfblog.me’,’https://nfblogs.com’); UPDATE wp_usermeta SET meta_value = replace(meta_value, ‘https://nfblog.me’, ‘https://nfblogs.com’); UPDATE wp_comments SET comment_content = REPLACE (comment_content,Continue reading “WordPress站点域名更换”
在CFW&CDN中通过Mixin配置Tun或Tap
本文由Telegram频道超超超超超超超超超菜的频道主编写~转载请注明出处~ 喜欢这篇文章请关注频道吧~ 本次介绍利用Clash for Windows(以下简称CFW)和Clash .Net(以下简称CDN)两个软件中的mixin功能实现TUN/TAP虚拟网卡接管流量 截至本文的修改完成,CFW的最新版本是0.15.6,CDN的最新版本是1.0.4 本次示例以便携版为示例,两种软件的便携化方法都是下载压缩包,在解压出来的文件夹中新建data文件夹,配置文件等都会保存在其中,更新只需要移动data文件夹,较为方便 1、TUN/TAP非常浅显的一点说明 (1)什么是TUN/TAP TUN是clash的premium核心专属功能,可以使用虚拟网卡接管流量 而TAP是CFW的功能,并非clash核心功能,同样可以使用虚拟网卡接管流量 (2)TUN/TAP有什么用 暂且不讨论对全局代理的理解(不知道我什么意思就当我没说好了) 本次介绍的两个客户端(CFW和CDN)都有系统代理的功能 但是系统代理并不能解决所有程序的代理问题 举例来说,当使用uwp应用的时候,默认是无法指定本地代理的;玩游戏的时候,是不经过代理的 传统方法是,对于uwp应用使用enableloopback,对于游戏使用Netch,还有人使用proxifier等 但我们可以使用TUN/TAP来建立虚拟网卡接管程序流量来达到强制代理的目的 也就是说,clash只要配置好,同样能完成别的软件可以完成的事 (3)一些杂谈 其实本次介绍的客户端中,CDN另有增强模式,使用增强模式同样能做到强制代理(效果与Netch的进程代理差不多) 增强模式与TAP一样,无法使用进程名规则PROCESS-NAME来进行分流,猜测可能是因为NetFilter在对流量进行传递的时候无法将进程名传递到Clash核心 本次暂且不对此进行讨论 2、什么是Mixin Mixin,你可以理解为临时覆写,或者可以理解为临时混合配置文件 Mixin的实质是在Clash核心读取完配置文件后对其进行临时修改而不对本地的原配置文件进行改动 利用这个特性,我们可以轻松实现TUN/TAP的临时开关(鉴于这两款软件并未原始提供默认的TUN/TAP配置和相应开关的选项) 3、CDN开启TUN的方法 (1)准备工作 目前TUN是Clash的Premium版核心专有功能,开源版核心并没有实现TUN CFW使用的是Premium核心,因此我们可以正常使用TUN 而CDN默认使用的是开源版核心,因此为了使用TUN我们应该更换核心为Premium版 我们先去Clash源仓库下载Premium核心(点此),向下翻找下载amd64(就是x64,如果你是x86或是说32位那就去下386) 注意,截至我编辑这篇教程的日期(2021/05/20),Clash的2021.05.08版Premium核心仍未修复2021.05.08版核心中TUN部分对Windows的支持的一些问题(相关链接:Github Issue),具体表现为TUN网卡无法正常地检测网络连接,网络中显示Clash网卡无法连接网络,这将导致微软系UWP应用无法正常使用,比如设置中的微软账户登录等,对此,我推荐去使用上文提到的Issue中维护者发出的包,或是选择回退2021.04.08版核心(可去旧版CFW客户端中的resource文件夹中获取) 以x64为例,我们下载到的文件解压后是这个(上图) CDN的核心文件存在于CDN目录下的/bin子文件夹,文件名是Clash.exe(下图)(由于本文从初次编写到最后修改有一段时间,此处的CDN版本并非最新,以下不作解释) 因此我们将原核心文件重命名为Clash.exebackup(以防下错核心从头再来),下载到的文件复制到该目录,并重命名为Clash.exe 然后我们去https://www.wintun.net下载wintun.dll(注:截至2021年5月20日,wintun已经更新到0.11) 由于我们是64位系统,所以打开压缩包里找wintun/bin/amd64下的wintun.dll,x86请使用wintun/bin/x86文件夹下的wintun.dll 然后我们在CDN目录建立一个data文件夹,打开CDN,过一会关闭CDN(生成目录) 然后将我们的wintun.dll复制到/data/clash中 之后我们退回到Clash.Net目录,右键Clash.Net是用管理员身份运行 UAC提示允许就完事了~ 如果讨厌每次打开都要点右键,可以右键点属性,在兼容性选项中勾选使用管理员身份运行 这边稍微介绍一下CDN的语言切换功能 只需在这个选项卡中切换语言即可。 打开后在CDN的界面的settings/config选项卡中点击编辑Mixin Content(中文混合配置内容编辑) 然后在弹出的编辑器中粘贴如下内容: 并保存(当然你如果明白参数的意思的话可以自己编辑mixin的内容,注意缩进) 在进行接下来的操作之前请务必保证你已经完成之前的准备工作 (2)开启Mixin 在进行接下来的操作之前请务必保证你已经完成之前的准备工作Continue reading “在CFW&CDN中通过Mixin配置Tun或Tap”
ServerStatus云探针安装教程
ServerStatus是一款适合监控多台服务器的探针程序,自带web前端界面;本篇教程采用的是 ServerStatus 的修改美化版,基于逗比大佬的一键脚本安装,可以让我们很方便上手,对于手上有很多台VPS服务器的MJJ们来说是个不错的监控工具。 安装要求:CentOS 7、Debian 7+、Ubuntu 14.04 +系统。 1、脚本分服务端和客户端,即一个主要提供web前段界面的VPS服务端,其它均为客户端,通过SSH工具连接上服务器,运行下列命令下载脚本: wget https://raw.githubusercontent.com/CokeMine/ServerStatus-Hotaru/master/status.sh && chmod +x status.sh 2、下载后根据自己的实际情况分别输入下列命令进行安装服务端和客户端: # 客户端管理菜单bash status.sh c # 服务端管理菜单bash status.sh s 3、首先我们这里示例安装服务端,输入数字1开始安装,然后输入服务端的监听端口,这个端口设置建议10000-60000之间即可,后面客户端连接需要用到,然后回车,安装过程中会提示是否配置HTTP服务,输入Y回车即可; 4、安装后进入服务端管理菜单选择7设置服务器配置,然后选择1添加节点,每次增加客户端都需要在这里添加一条节点; 5、接着输入节点的信息,也就是要监控的服务器名称等,后面客户端连接需要用到; 客户端连接,在需要监控的服务器上安装上面的脚本,然后输入命令进入客户端管理界面,选择安装,接着配置下服务端的IP/域名以及前面设置的监听端口,节点信息等等,如下图: 其它说明: 安装目录:/usr/local/ServerStatus 网页文件:/usr/local/ServerStatus/web 配置文件:/usr/local/ServerStatus/server/config.json 增加多个服务器节点,先在服务端添加节点,然后再到其它服务器上面部署连接客户端 修改网页标题或者网页顶部公告内容,打开服务端/usr/local/ServerStatus/web/index.html文件修改即可 本文转载自 https://zhujiget.com/3657.html
WordPress用户角色权限详解
WordPress作为全球用户最多的CMS建站系统,内置不同级别的WordPress用户角色供用户使用。WordPress用户角色是指用户访问网站及后台管理操作的权限级别,每个用户角色都有对应不同的权限和功能。由于不同角色对应的用户权限不同,这样可以有效的控制网站的后台操作访问,尤其是在进行WordPress多用户管理网站的时候,WordPress的多用户角色权限功能是非常有用的。WorPress的这些用户角色权限和功能的分配,可以基本满足一般网站所需的用户操作权限方面的功能要求。 WorPress用户角色 WordPress默认内置了五种用户角色(Roles),级别由高到底分别是: Administrator(管理员) Editor(编辑) Author(作者) Contributor(贡献者或投稿者) Subscriber(订阅者) 这五种用户角色类型分别拥有不同的网站权限和作用,WordPress在安装完毕之后,会为各个用户角色分配默认的用户能力/权限,也共同构成了WordPress小巧却强大的用户系统。WordPress默认的五种用户角色权限列表: WordPress用户角色权限列表 1. Administrator(管理员) WordPress用户角色中的管理员拥有网站的全部权限,可以操作访问WordPress网站后台的每一个页面和功能,是最强大的用户角色。在WordPress安装完成后,作为管理员登录WordPress网站后台就可以操作设置网站每一项功能了。管理员除了可以添加新用户,更改编辑用户信息外,还可以更改和删除其他用户的用户角色。因为这个用户角色的权限最高,所以一般管理员用户都只能有1~2个,保证网站的安全性。 2. Editor(编辑) 编辑可以管理、更新、维护网站内容,包括添加发布或编辑增删文章,还可以管理媒体库和评论,对文章标签、分类进行管理,可以管理友情链接,编辑评论,添加或编辑页面,还可以编辑其他人的文章。但不可以进行网站设置,管理主题和插件,也不能添加编辑或删除其他用户角色。 3. Author(作者) 作者可以编辑和发布自己的页面和文章,而且,作者发表的文章不需要审核即可正常发布,另外,作者还可以使用媒体库上传图片和视频等文件,也可以编辑和删除自己发布的内容,但不能编辑和删除其他人发布的内容。 4. Contributor(投稿者) 投稿者可以发表文章和评论,但发表的文章必须经过网站管理员审核通过后才能正式发布;对自己待审的文章可以进行编辑和删除,通过审核的文章则不可以再编辑和删除。可以编辑自己的个人资料信息,如昵称,密码,联系信息。 5. Subscriber(订阅者) 订阅者是权限最小的用户角色,只能编辑自己的个人资料信息,如昵称,密码,联系信息。 这五种WordPress用户角色在网站运营过程中各自发挥着特定的作用,各司其职,使得网站可以安全高效地维护和运行。 转载自 https://www.wppop.com/wordpress-user-roles.html