TensorFlow上手(2) 第一个程序

基本使用

以下是来自 TensorFlow 中文社区的对 TensorFlow 工作流程的解释:
使用 TensorFlow, 你必须明白 TensorFlow:

  • 使用图 (graph) 来表示计算任务。
  • 在被称之为 会话 (Session) 的上下文 (context) 中执行图。
  • 使用 tensor 表示数据。
  • 通过 变量 (Variable) 维护状态。
  • 使用 feed 和 fetch 可以为任意的操作(arbitrary operation) 赋值或者从其中获取数据。

有点拗口,但可以将其理解为工厂中,原料由tensor提供,工艺流程由图设计,一些机器可以维护现场状态或数据,由变量提供,而 feed 和 fetch 是人工的工具。

Jupyter 打开

在浏览器中,打开 Jupyter 工作环境,新建一个文件(新建过程请查阅 引用3),开始我们的 tf 之旅。

构建图

构建图的第一步, 是创建源 op (source op)。源 op 不需要任何输入, 例如 常量 (Constant)。 源 op 的输出被传递给其它 op 做运算。

1
2
3
4
5
6
7
8
9
import tensorflow as tf
# 创建一个常量 op,值为 2
v1 = tf.constant(2)
# 创建一个常量 op,值为 3
v2 = tf.constant(3)
# 创建一个乘法 op,
# 把 'v1' 和 'v2' 作为输入
# 返回值 'res' 代表乘法的结果
res = tf.multiply(v1,v2)

默认图现在有三个节点,两个 constant() op,和一个mul() op。 为了真正进行相乘运算,并得到乘法的结果,你必须在会话里启动这个图。

在一个会话中启动图

构造阶段完成后,才能启动图。启动图的第一步是创建一个 Session 对象,如果无任何创建参数,会话构造器将启动默认图。

1
2
3
4
5
6
7
8
# 调用sess的 run() 方法来执行乘法 op, 传入 'res' 作为该方法的参数。
# 整个执行过程是自动化的, 会话负责传递 op 所需的全部输入. op 通常是并发执行的。
# 函数调用 run(res) 触发了图中三个 op (两个常量 op 和一个乘法 op) 的执行。
result = sess.run(res)
print result
# ==> 6
# 任务完成, 关闭会话.
sess.close()

Session 对象在使用完后需要关闭以释放资源。除了显式调用 close 外,也可以使用 with 代码块来自动完成关闭动作。

1
2
with tf.Session() as sess:
print(sess.run(res))

在实现上,TensorFlow 将图形定义转换成分布式执行的操作,以充分利用可用的计算资源(如 CPU 或 GPU)。一般你不需要显式指定使用 CPU 还是 GPU,TensorFlow 能自动检测。如果检测到 GPU,TensorFlow 会尽可能地利用找到的第一个 GPU 来执行操作。
如果机器上有超过一个可用的 GPU,除第一个外的其它 GPU 默认是不参与计算的。为了让 TensorFlow 使用这些 GPU,你必须将 op 明确指派给它们执行。
设备用字符串进行标识,目前支持的设备包括:

  • "/cpu:0":机器的 CPU。
  • "/gpu:0":机器的第一个 GPU,如果有的话。
  • "/gpu:1":机器的第二个 GPU,以此类推。
    with...Device 语句用来指派特定的 CPU 或 GPU 执行操作:
    1
    2
    3
    4
    5
    6
    with tf.Session() as sess:
    with tf.device("/gpu:1"):
    matrix1 = tf.constant([[3., 3.]])
    matrix2 = tf.constant([[2.],[2.]])
    product = tf.matmul(matrix1, matrix2)
    ...

Fetch

Fetch 可以帮助我们从 Session 中取回多个结果,在之前的例子中,我们只取回了一个 res 的结果,下个例子中,我们用 Fetch 来一次性获得多个结果。

1
2
3
4
5
6
7
8
9
10
import tensorflow as tf
v1 = tf.constant(2)
v2 = tf.constant(3)
res1 = tf.multiply(v1,v2)
res2 = tf.add(v1,v2)

with tf.Session() as sess:
res = sess.run([res1, res2])
print(res)
# ==> [6, 5]

Feed

上面的例子在计算图的时候用到了 tensor,都是以常量或变量的形式存储的,而同时 TensorFlow 还提供了 Feed 机制,来为临时替换图中任意操作的 tensor。

Feed 使用一个 tensor 值临时替换一个操作的输出结果。你可以提供 feed 数据作为 run() 调用的参数。Feed 只在调用它的方法内有效,方法结束,feed 就会消失。
在使用 Feed 之前,我们要进行标记,标记的方法是使用 tf.placeholder() 为这些操作创建占位符。

1
2
3
4
5
6
7
v1 = tf.placeholder(tf.float32)
v2 = tf.placeholder(tf.float32)
output = tf.multiply(input1, input2)

with tf.Session() as sess:
print(sess.run(output, feed_dict={input1:[7.0], input2:[2.0]}))
# ==> [14.]

#引用

  1. https://www.jianshu.com/p/eaee1fadc1e9
  2. http://www.tensorfly.cn/tfdoc/get_started/basic_usage.html
  3. https://blog.csdn.net/red_stone1/article/details/72858962

TensorFlow上手(1) 环境配置

TensorFlow 简介

TensorFlow 最初由Google大脑小组(隶属于Google机器智能研究机构)的研究员和工程师们开发出来,用于机器学习和深度神经网络方面的研究,但这个系统的通用性使其也可广泛用于其他计算领域。
Tensor的意思是张量,代表N维数组;Flow的意思是流,代表基于数据流图的计算。把N维数字从流图的一端流动到另一端的过程,就是人工智能神经网络进行分析和处理的过程。

配置环境

本文章是在 Windows 10 (1803) 下进行的配置,以 Python 语言作为工作语言。

下载 Anaconda

Anaconda指的是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项,方便我们后面的配置。

官网上,当下最新的版本 Anaconda 5.2 For Windows,可使用 Python 3.6。

下载并安装完成后我们要进行配置。

配置 Anaconda

安装完 Anaconda 后,将 (安装目录)\Anaconda3\Scripts 加入系统环境变量 Path 中,网上相关教程多,这里按下不表,然后我们的操作都要在命令行下进行。

以管理员权限打开命令行,输入 conda -Vconda --version 显示当前安装的版本,我这里是 4.5.8 ,若无错误则说明上述变量加入无误,接下来输入conda upgrade --all 把所有工具包进行升级,避免后续出现问题。

接下来要给 Python 建立一个独立的环境,输入 activate ,发现命令行前多出了 (base) , 这是 Anaconda 自带的环境。此时若输入 python 就可以进入 Anaconda 自带的 Python 环境,这个环境是与你之前有无安装 Python 无关的,同时此时输入 python -V 就可以查看此时 python 的版本。

接下来创建一个自己的虚拟环境,我们输入以下代码

1
conda create -n tfLearn python=3

创建一个名称为 tfLearn 的虚拟环境,使用的 python 版本是 python3,你也可以细化到 python=3.x.x
我们可以使用

1
conda env list

列出所有环境的名称和所在位置。

此时我们用下列语句切换至我们的 tfLearn 环境。

1
activate tfLearn

此时命令行前的 (base) 改变成了 (tfLearn) ,表明切换成功,下一步我们进行 python 的配置。

配置 python

进入 tfLearn 环境后,我们使用下列安装 tensorFlow。

1
pip install tensorflow-gpu==1.9.0

注意此处指定了 tensorflow 是以 gpu 方式安装的,且版本为 1.9.0 ,这是为了与后面的 CUDA 配套的。

如果此时你不是以管理员权限进行的安装的话,在此处安装结尾可能会出现报错。

安装 CUDA 和 cuDNN

NVIDIA官网中CUDA下载
请选择 CUDA 9.0,这是配套的。

NVIDIA官网中cuDNN下载
请选择 cuDNN v7.1.4 for CUDA 9.0,解压后放置于CUDA v9.0的根目录下(文件夹刚好是对应的)。

测试环境

tfLearn 环境下,进入 python 环境,输入

1
import tensorflow as tf

经过一小段等待,没有报错信息输出,说明安装成功,我们的测试环境就搭建好了。

安装jupyter notebook

在 Anaconda 页面中,在 Home选项卡下,将 Applications on 切换到 tfLearn 环境,下面会出现一些工具可以安装,我们找到 jupyter notebook 点击 install ,完成后点击 Launch,浏览器会默认打开 http://localhost:8888 这是 jupyter notebook的工作环境,也是我们之后要实践的主要场地。

Anaconda 意义

Anaconda 是一个多环境的配置工具,他解决我们在工作中不同工具版本的共存问题。
他实现这个解决方法的方式十分简单,我们的新建的一个个工作环境都在 Anaconda3\envs 下,点开我们的 tfLearn 文件夹,里面就是一个完备的 python 环境。


#引用

  1. https://www.jianshu.com/p/eaee1fadc1e9

美文系列:滕王阁序(1)

本文系转载,已征得原文作者同意,特此声明。
原文作者:海上钢琴师
原文链接:http://www.acfun.cn/a/ac4159097


豫章故郡,洪都新府。星分翼轸,地接衡庐。襟三江而带五湖,控蛮荆而引瓯越。物华天宝,龙光射牛斗之墟;人杰地灵,徐孺下陈蕃之榻。雄州雾列,俊采星驰。台隍枕夷夏之交,宾主尽东南之美。都督阎公之雅望,棨戟遥临;宇文新州之懿范,襜帷暂驻。十旬休假,胜友如云;千里逢迎,高朋满座。腾蛟起凤,孟学士之词宗;紫电青霜,王将军之武库。家君作宰,路出名区;童子何知,躬逢胜饯。

如果要评选古代最美的文章,那《滕王阁序》即使不能夺魁,也一定是在三甲之内。这篇文章,可以说是古代骈文中的最高峰、集大成之作。此前、此后,都没有能在骈文之道上超过这一篇的。

骈文是一种极端华丽的文体。这种文体发端于汉魏,盛行于南北朝,特点是每一句都像对联一样,严格对仗。同时,多使用四字、六字的句子,让整体韵律更加周密协调。为了让内容更有深意,骈文还讲究多用典故,每一句里都隐含着各种历史故事。整篇下来,就仿佛是精雕细琢、嵌满珠玉、金碧辉煌的宫殿一般,给人以目不暇接之感。

但可想而知,这种文章非常难写。就像最近大热的《国家宝藏》节目里,大家一起嘲讽乾隆的各种釉彩大瓶一样——骈文因为太华丽,所以写起来往往顾此失彼:注重内涵了,结果文辞不够华美;雕琢文采了,结果空洞无味;典故用少了,读来就有村气;典故用多用僻了,大家又觉得费解。要把握好其中的平衡,写出一篇好的骈文,简直难如登天。

不过,对于真正的天才来说,这些都不是事儿啊!

王勃就是这样的天才。作为初唐四杰王杨卢骆之首,王勃的才气得到了当时人们的一致认可,他们四位是承上启下、开辟了唐代文学盛世的重要人物,其才能可见一斑。

王勃少年成名,十岁前通读经书,十岁后就已经因诗文而名动京师了。虽然他不到三十岁就意外身故,但留下的一系列诗文足以让他列入唐代最顶尖的文学家行列。而他最出色的作品,就是这篇《滕王阁序》。

好在哪里?咱们一起来读一读吧!

豫章故郡,洪都新府。星分翼轸,地接衡庐。

这是开篇四句。滕王阁在江西南昌,紧靠赣江,号称江南楼阁之首。这座楼是唐初一位亲王修的,他的封号是滕王,所以这座楼就叫滕王阁。

南昌是一座极有历史的古城。汉代这里叫豫章,唐代这里叫洪州,所以前两句直接从历史角度描述了这座城市在哪里。豫章故郡,说明过去这里是古豫章所在。洪都新府,说明这里是当今的新洪州城。历史和现代,在这里交汇在一起。

描述了时间的坐标,接下来就是空间的坐标。中国古代将星空也分为不同的星宿,相当于现在的星座,并且将这些星宿和古中国的地理划分一一对应起来。南昌一带,刚好对应天上翼宿和轸宿之间,所以说星分翼轸。

在天为翼轸之间,在地刚好是衡山和庐山之间,所以紧跟着说地接衡庐。一在天,一在地,完美的描述了南昌的空间坐标。于是,通过短短十六个字,南昌城的时空一下子就确定了。

襟三江而带五湖,控蛮荆而引瓯越。

有了时空的位置,那这个城市的意义呢?跟下来两句立刻就解释了。南昌紧靠赣江,刚好是三条支流交汇之处;五湖,这个一般来说没有具体的指代,但我认真的在地图上看过,貌似还真是有五个:

(自己找一找,三江和五湖都在哪里呢?)

控蛮荆而引瓯越,这里面就指出了江西的重要性。江西西接湖南湖北,这一片是历史上的楚国、三国时的荆州。楚王自己说“我蛮夷也”,所以这里用蛮荆来指代这一片楚地。楚人善战,江西居其下游起到抑制作用,所以用控字表达江西对两湖地区的控制力。而江西往东,就是浙江一带,乃是古时候的越国。古越国都城为东瓯,金瓯一词还有国土一角的意思,所以瓯越包含了这两重含义。江西居于越国上游,所以用引字,表达两地的关系。

综合起来,这两句先描绘了南昌的地形,又赞扬了地理位置之重要,一下子就把南昌城的意义捧得很高了。

物华天宝,龙光射牛斗之墟;人杰地灵,徐孺下陈蕃之榻。

这两句里,创造出了两个常常连用的成语:物华天宝、人杰地灵。物华天宝,指这里的出产极其美好,仿佛都是来自天上的宝物一般。光吹不行,后面就是个具体的例子——龙光射牛斗之墟。

这是个晋代故事。西晋时有位大牛人张华,这哥们不仅官做的大,学问更是高深,乃是中国历史上第一位博物学家。据说他当时看到天上的牛宿和斗宿之间常有紫光闪烁,于是就问朋友雷焕是什么原因。雷焕说,这一定是地上有奇珍异宝,宝光冲天,所以看到天上都有紫光。看这个位置,宝物应该是埋藏在豫章一带。后来,他们果然在豫章郡治下的丰城县发掘出一个石匣,其中是两把宝剑,一名龙泉,一名太阿。后来,这两把剑落入水中,人们下去打捞的时候没有看到宝剑,却看到两条蛟龙蜿蜒盘旋,从此这两把剑就不知所踪了。

所以你看,我说这地物华天宝,这是真的啊!挖出来两把剑都能化龙!

人杰地灵,是说在这片充满灵气的土地上,盛产各种人才。同样,后面也是一个具体的例子。陈蕃是汉末名士,著名的“一屋不扫何以扫天下”就是他小时候说的。后来他在豫章做官的时候,和豫章名士徐孺子交好。好到什么程度呢?他平时根本不接待其他客人,但专门给徐孺子打了一张床榻,每当徐孺子来的时候,两人就躺在这张专床上通宵达旦的聊天……

Emmmmmm……

不过不管这个故事究竟有什么含义,江西确实是个出才子的地方,所以这一句人杰地灵,也仿佛预示了江西后来井喷式出现的各类人才,实在是精辟的很。

这两句连起来,就又把江西和南昌捧到了一个新的高度。

为什么要捧呢?其实原因很简单——王勃这是客场作战。王勃不是江西人,和当时滕王阁上的一大群名流也没有交集。这是以当时的洪州都督、州牧为首所发起的一场上流社会party,王勃因为当时去南方探望自己外地做官的父亲,刚好路过南昌,因为文名而被邀请参加的。

据传说,当时本来是都督阎公让自己的女婿事先做好了一篇赋,准备在聚会上抛出来。结果在聚会上,阎公客套了一句:“哪位高才,愿意做赋而纪之啊?”,按照剧本,大家也都知道这时候都该谦让一番,最后公推阎公女婿出来执笔,给这场盛会划上句号。

结果,十几岁的王勃不知道还有这么多门道,一听要写文章,那当然是我来啊!于是就直接跳了出来,表示既然如此,那我这个高才就来写一篇吧!

当时阎公脸就黑了,但又不能自己打脸,于是就表示行啊小伙子,那你有胆你就写写看,然后拂袖而去,到楼上雅间里生闷气去了。

王勃这时候终于感觉出不对来了……看来不小心踩了个大坑啊。怎么办,在别人的场子上打了主人的脸,换了一般人,这时候估计就要屁滚尿流落荒而逃了,先保住自己这条狗命再说。

但王勃不是一般人。在这样的压力之下,他反而文思泉涌——既然已经打了脸,那我干脆就把这脸打的漂漂亮亮的,让你们不得不服,而且还要亲口说打的好!不过大家面子上还是要过得去的,我在文章里捧你们一下,给主人修个好看的台阶,看你怎么办。

于是,接下来几句,更是捧到了极致。

雄州雾列,俊采星驰。台隍枕夷夏之交,宾主尽东南之美。

雄州雾列,是说洪州乃是大唐的一等雄州,远眺山川,楼宇屋舍田地连绵不绝,一直隐没到远方的薄雾之中。俊采星驰,是说当地俊杰犹如星辰一般繁多、闪耀、在这片丰饶的土地上往来驰骋,使人目不暇接。著名影星周先生,其名字就是来自这个词。

(雄州雾列,大致就是这个feel)

台隍枕夷夏之交,夷是蛮夷,夏是华夏。这句呼应了上面对江西地理位置重要性的描述,更进一步的点出了这座楼台、这座城池就位于蛮夷文明和中原文明的分界点上,乃是照亮蛮荒之地的明灯。而今天在这里的宾客和主人呢?也自然是集中了东南地区最杰出的人士,所以是宾主尽东南之美。

都督阎公之雅望,棨戟遥临;宇文新州之懿范,襜帷暂驻。

为了怕在座各位不明白,我王勃再强调一下,我们要紧密团结在阎都督和宇文州牧的领导周围!阎都督素有雅望,德高望重,乃是天下名士。棨戟是古时候大官出行时,队伍前面举的仪仗。遥临两字,更是说明了阎都督是不远千里来这里任职指导工作、给我们指明前进道路的,大家更应该有感恩之心才对啊。

而宇文州牧,也是我们做官做人之懿范,是值得我们学习的好榜样。而且宇文州牧只是在这里暂时任职——你问为什么是暂时?废话,那当然是以后很快就要高升啊!

估计阎公看到这两句,脸色就很难再黑起来了。虽然这个年轻人有点狂傲,但还是很懂得分寸的嘛!

十旬休假,胜友如云;千里逢迎,高朋满座。

这两句指出了这次聚会的原因,还贡献了高朋满座这个成语。大家总不能没事工作日聚餐,这次聚会,是因为刚好赶上了周末——唐代以十天为一旬,旬末一天是休息日。恰好赶上很多人杰聚集于此,所以大家才举行了这场聚会。为了这次盛大的聚会,有些朋友(比如我)甚至是不远千里而来的,大家坐满了席位,好一派盛况!

腾蛟起凤,孟学士之词宗;紫电青霜,王将军之武库。

因为席上还有几位地位很高的朋友,所以再特意给大家点出来,尤其是咱们江西的文学领袖孟学士和军队领导王将军。孟学士是一代诗词文学大家,其文章如同蛟龙和凤凰一样绚烂。王将军的武艺非常出色,在武库里收藏的神兵如同古代名剑紫电、青霜一般名贵。因为朋友太多,这里就不再每一位都列出来了,还请大家见谅,谁有不同意见,可以和孟学士、王将军谈一谈。

家君作宰,路出名区;童子何知,躬逢胜饯。

在第一段的最后,捧完这座城市、捧完在座的诸位大佬、满座贤达以后,王勃点出了自己的身份。我也不是个毫无背景的人,我爸也是在外做官、管理一方的。这是在去探亲的路上,我恰好经过了咱们这座名城。大家都是有身份的人,就别小看我、难为我了吧?

童子两字说出了王勃的年龄。历史上对于王勃是哪一年写的《滕王阁序》有两种说法,一说是十多岁的时候,一说是二十多岁,双方各有依据,争论不休。但我个人更倾向于相信十多岁的说法,一是因为这样更有传奇性,二是很难相信二十多岁、早就考取功名并任过官职的王勃还要自称为童子。

所以,这最后一句也是谦让。诸君,我只是个十多岁的无知小朋友,恰好有幸遇到这次盛宴。所以,如果有哪里做的不到位的、如果后面的文字里有什么小小得罪的地方,还请大家体谅。

回头再看这第一段,虽然篇幅精炼,但把这场盛会的来龙去脉、地点、原因都说的一清二楚,同时还对江西、对南昌、对主人、对各位宾客吹捧的极为到位。凭心而论,虽然南昌很好、虽然在座的也确实是当时当地之俊才,但大家看到这样的夸赞后,怕不是还是会有一丝脸红,心里有几分窃喜的。纵然大家再有什么被抢了风头的不快,看到这样的文笔、看到这样的褒扬,也没有办法再发作了。不仅如此,这个颇有气势的开场,更是令人对后面的几段产生了莫大的期待。大江之畔、高楼之上,估计宾客们正在屏息静待,看这个十多岁的少年还能抛出多么灿烂的文字。

那么,接下来,前言结束,我这就开始表演了!

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

More Try

1
2
3
4
5
6
7
8
protected class A : public C{
public:
int X() {
return x;
}
private:
int x;
}

中文

加粗

分割


重点

表格1 表个
dadaa
sdaa ddddd

$$
F_x = a^2 + b^2
$$

欧拉计划个人题解(021-025)

[021]Amicable numbers

题意

在正整数数域内,有一个数x,他的所有因子之和(不包括自身),等于另一个数y($y \neq x$);同时,y的所有因子和(不包括自身),等于数x。则x和y称为亲和数对,x和y都属于亲和数。

现求1到10000内所有亲和数之和。

思路

对每个数进行整数乘法,统计到对应数的因子和,然后再进行线性扫描即可。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const int MAXN = 10000;
int num[MAXN + 1];
int main(void)
{
memset(num,0,sizeof(num));
for(int i = 1; i <= MAXN; ++i)
{
for(int j = i * 2; j <= MAXN; j += i)
num[j] += i;
}
int ans = 0;
for(int i = 1; i <= MAXN; ++i)
{
if(num[i] <= MAXN && num[ num[i] ] == i && num[i] != i) // "num[i] != i" is important
ans += i;
}
cout << ans << endl;
return 0;
}

答案

31626

[022]Names scores

题意

给一个只包含大写英文的名(first name)文件,根据他们的位置,计算他们的价值,他们的价值定义为:每一位字母的在字母表中的序号之和与该姓名在所有名中的位置之乘积。
现求他们的价值和。

思路

读取直接做,C++的格式化读入还是比较麻烦。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
string next(ifstream& in)
{
vector<char> buffer;
char tmp = -1;
while(in >> tmp && tmp != '"');
if(tmp == -1)
return string(1,'\0');
in >> tmp;
do{
buffer.emplace_back(tmp); // There would not read '"' into buffer.
in >> tmp;
}while(tmp != '"');
string str(buffer.size(), '\0');
auto viter = buffer.begin();
auto siter = str.begin();
while(viter != buffer.end() && siter != str.end())
*siter++ = *viter++;
return str;
}
int main()
{

ifstream dataStreamer;
dataStreamer.open("p022_names.txt", ios::in, _SH_DENYWR);
if(!dataStreamer)
{
cout<<"文件读错误";
system("pause");
exit(1);
}

set<string> s;
while(true)
{
string str = next(dataStreamer);
if(str.size() == 1)
break;
s.insert(str);
}
dataStreamer.close();
int ans = 0;
{
int cnt = 1;
for(string e : s)
{
int tmp = 0;
for(char c : e)
tmp += c - 'A' + 1;
ans += cnt * tmp;
++cnt;
}
}
cout << ans << endl;
return 0;
}

答案

871198282

[023]Non-abundant sums

题意

28的因数为1,2,4,7,14。1+2+4+7+14=28,称28为完美数。
已知n,其因数和sum大于n,称n为多余数。12是最小的多余数,24是最小得可以有两个多余数相加。超过28123的任何数都可以写成两个多余数相加。
那么找出所有的无法由两个多余数组成的正整数之和。

思路

先建一个数组确认有哪些是多余数,再建一个数组确认这些多余数可以组成什么数,注意一个数可以被两个同样的多余数相加得到。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int abundantNumberCheck[28123 + 1];
bool abundantNumberMixed[28123 + 1];
int main()
{
memset(abundantNumberCheck, 0, sizeof(abundantNumberCheck));
memset(abundantNumberMixed, 0, sizeof(abundantNumberMixed));
vector<int> abunantNumber;
for(int i = 2;i <= 28123; ++i)
{
for(int j = i * 2; j <= 28123; j += i)
abundantNumberCheck[j] += i;
if(abundantNumberCheck[i] > i)
abunantNumber.push_back(i);
}

for(size_t v1 = 0; v1 < abunantNumber.size(); ++v1)
for(size_t v2 = v1; v2 < abunantNumber.size(); ++v2) // it means that a number can be expressed two same numbers
{
if(abunantNumber[v1] + abunantNumber[v2] > 28123)
break;
abundantNumberMixed[ abunantNumber[v1] + abunantNumber[v2] ] = true;
}
int sum = 0;
for(int i = 1; i < 28123 + 1; ++i)
{
if(abundantNumberMixed[i] == false)
sum += i;
}
cout << sum << endl;
return 0;
}

答案

4179871

[024]Lexicographic permutations

题意

找0、1、2、3、4、5、6、7、8、9这些数组合成一个数进行字典序排序后第一百万个数。

思路

DFS,先确认第一个数,这样可以算出后面有多少种情况,若第一百万不在里面就要调整第一个数,依次推类直到最后一个数字。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const int TARGET = 1e6;
const int RANGE = 10;
int getPro(int n)
{
int ans = 1;
for(int i = n; i > 1; --i)
ans *= i;
return ans;
}
list<int> num;
void dfs(int target, int p, queue<int> &s)
{
if(p == 1)
{
s.push(*num.begin());
return ;
}
int part = getPro(p-1);
cout << "part" << part << endl;
auto i = num.begin();
for(;i != num.end(); ++i)
if(target <= part)
{
cout << *i << "b" << endl;
break;
}
else
target -= part;
s.push(*i);
num.erase(i);
dfs(target, p-1,s);
}

int main(void)
{
queue<int> s;
for(int i = 0; i < RANGE; ++i)
num.push_back(i);
dfs(TARGET, RANGE, s);
long long ans = 0;
while(!s.empty())
{
cout << s.front() << "xxx" << endl;
ans = ans * 10 + s.front();
s.pop();
}
cout << setfill('0') << setw(10) << ans << endl;
return 0;
}

答案

2783915460

[025]Lattice paths

题意

斐波那契数列:

$$
F_1 = 1 \\
F_2 = 1 \\
F_3 = 2 \\
F_n = F_{n-1} + F_{n-2} \\
$$
先要你求x,$F_x$的数位数第一次超过1000。

思路

高精度直接求就行,用我之前的模版,此处略模版细节。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
BigInt f[] = {BigInt::One(), BigInt::One(), BigInt::One()};
int cnt = 0;
while(f[cnt % 3].size() < 1000)
{
f[cnt % 3] = f[(cnt+1) % 3] + f[(cnt+2) % 3];
++cnt;
}
cout << cnt << endl;
return 0;
}

优化

自我考虑的,尚未实现:

已知 $log_{10}x$ 可以求x这个数字在十进制下的位数,我们又已知斐波那契数列的通项公式 $f(x)$ ,则 $1000 < log_{10}f(x)$ ,此时的x就是所求答案,但是斐波那契的 $(1+\sqrt{5})^x - (1-\sqrt{5})^x$ 我无法化成一个数字,将x这个项数用log的公式换下来。

答案

4782

欧拉计划个人题解(016-020)

[016]Power digit sum

题意

求$2^{1000}$的每数位之和。

思路

高精度乘法,并利用快速幂优化。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
class BigInt
{
public:
BigInt(int num);
BigInt(const BigInt&);
BigInt(BigInt&& o) noexcept;
BigInt& operator = (const BigInt&);
static BigInt Zero();
static BigInt One();

bool operator == (const BigInt&) const;
BigInt& operator ++(); //++i
BigInt& operator --(); //--i
BigInt operator +(const BigInt&);
BigInt operator +=(const BigInt&);
BigInt operator *(const BigInt&);
BigInt operator *=(const BigInt&);

inline size_t& size();
inline size_t size() const;
string toString() const;
~BigInt();

friend ostream& operator << (ostream& out, const BigInt& o)
{
auto ptr = o.m_num + o.size();
do{
--ptr;
out << *ptr;
}while(ptr != o.m_num);
return out;
}
private:
BigInt();
int* m_num;
size_t m_size;
void format();
};
size_t& BigInt::size()
{
return m_size;
}
size_t BigInt::size() const
{
return m_size;
}
BigInt::BigInt()
{
}
BigInt::~BigInt()
{
if(m_num != nullptr)
delete[] m_num;
m_size = 0;
}
BigInt::BigInt(int num)
{
size_t cnt = 0;
{
int tmp = num;
do{
++cnt;
tmp /= 10;
}while(tmp != 0);
}
size() = cnt;
m_num = new int[size()];
auto ptr = m_num;
do{
*ptr = num % 10;
num /= 10;
++ptr;
}while(num != 0);
}
BigInt::BigInt(const BigInt& o)
{
size() = o.size();
m_num = new int[size()];
memcpy(m_num, o.m_num, size() * sizeof(int));
}
BigInt::BigInt(BigInt&& o) noexcept
{
size() = o.size();
m_num = o.m_num;
//delete;
o.size() = 0;
o.m_num = nullptr;
}
BigInt& BigInt::operator = (const BigInt& o)
{
size() = o.size();
if(m_num != nullptr)
delete [] m_num;
m_num = new int[size()];
memcpy(m_num, o.m_num, size() * sizeof(int));
return *this;
}
BigInt BigInt::Zero()
{
BigInt a(0);
return a;
}
BigInt BigInt::One()
{
BigInt a(1);
return a;
}
bool BigInt::operator==(const BigInt& o) const
{
if(size() != o.size())
return false;
for(size_t i = 0; i < size(); ++i)
if(m_num[i] != o.m_num[i])
return false;
return true;
}
BigInt& BigInt::operator ++()
{
++m_num[0];
format();
return *this;
}
BigInt& BigInt::operator --()
{
--m_num[0];
format();
return *this;
}
void BigInt::format()
{
// Carry
for(size_t i = 0; i < size() - 1; ++i)
if(m_num[i] >= 10)
{
m_num[i+1] += m_num[i] / 10;
m_num[i] %= 10;
}
else
if(m_num[i] < 0)
{
int cnt = (m_num[i] / 10 + 1);
m_num[i] += cnt * 10;
m_num[i+1] -= cnt;
}
// Stretch out digital
if(m_num[size() - 1] >= 10)
{
int* new_num = new int[size() + 1];
memcpy(new_num, m_num, size() * sizeof(int));
new_num[size()] = new_num[size() - 1] / 10;
new_num[size() - 1] %= 10;
++size();
if(m_num != nullptr)
delete [] m_num;
m_num = new_num;
}
// Draw back digital
if(size() == 1)
return;
auto ptr = m_num + size();
do{
--ptr;
if(*ptr != 0)
break;
}while(ptr != m_num);
size_t new_size = static_cast<size_t>(ptr - m_num + 1);
if(size() != new_size)
{
int* new_num = new int[new_size];
memcpy(new_num, m_num, new_size * sizeof(int));
if(m_num != nullptr)
delete [] m_num;
m_num = new_num;
size() = new_size;
}
}
BigInt BigInt::operator +(const BigInt& o)
{
BigInt ans;
size_t length = max(size(), o.size());
ans.m_num = new int[length];
ans.size() = length;
for(size_t i = 0; i < min(size(), o.size()); ++i)
ans.m_num[i] = m_num[i] + o.m_num[i];
if(size() > o.size())
for(size_t i = o.size(); i < size(); ++i)
ans.m_num[i] = m_num[i];
else
for(size_t i = size(); i < o.size(); ++i)
ans.m_num[i] = o.m_num[i];
ans.format();
return ans;
}
BigInt BigInt::operator+=(const BigInt& o)
{
if(size() < o.size())
{
int* new_num = new int[o.size()];
memcpy(new_num,m_num,size());
for(size_t i = size(); i < o.size(); ++i)
new_num[i] = 0;
delete [] m_num;
m_num = new_num;
}
for(size_t i = 0; i < o.size(); ++i)
m_num[i] += o.m_num[i];
format();
return *this;
}
BigInt BigInt::operator *=(const BigInt& o)
{
size_t new_size = size() + o.size() - 1;
int* new_num = new int[new_size];
memset(new_num, 0, new_size * sizeof(int));
for(size_t i = 0; i < size(); ++i)
for(size_t j = 0; j < o.size(); ++j)
new_num[i + j] += m_num[i] * o.m_num[j];
size() = new_size;
delete [] m_num;
m_num = new_num;
format();
return *this;
}
BigInt BigInt::operator *(const BigInt& o)
{
BigInt ans;
size() = size() + o.size() - 1;
ans.m_num = new int[size()];
memset(ans.m_num, 0, size() * sizeof(int));
for(size_t i = 0; i < size(); ++i)
for(size_t j = 0; j < o.size(); ++j)
ans.m_num[i + j] += m_num[i] * o.m_num[j];
ans.format();
return ans;
}
string BigInt::toString() const
{
char* str = new char[size() + 1];
for(size_t i = 0; i < size(); ++i)
str[size() - i - 1] = static_cast<char>(m_num[i] + '0');
str[size()] = '\0';
string ans = string(str);
delete str;
return ans;
}
BigInt mulpow(BigInt base, int index)
{
BigInt ans = BigInt::One();
while(index != 0)
{
if(index & 1)
ans *= base;
index >>= 1;
base *= base;
}
return ans;
}
int main()
{
BigInt ans = mulpow(BigInt(2), 1000);
string str = ans.toString();
int count = 0;
for(auto iter = str.begin(); iter != str.end(); ++iter)
count += (*iter) - '0';
cout <<count << endl;
return 0;
}

答案

1366

[017]Number letter counts

题意

统计1到1000的数字,英文写法的字符和(包括连字符)。

思路

根据规律统计。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
个位数:
zero 4
one 3
two 3
three 5
four 4
five 4
six 3
seven 5
eight 5
nine 4
两位数:
ten 3
eleven 6
twelve 6
thirteen 8
fourteen 8
fifteen 7
sixteen 7
seventeen 9
eighteen 8
nineteen 8
twenty 6
twenty-one 9
...
thirty 6
forty  5
fifty  5
sixty  5
seventy  7
eighty  6
ninety  6
三位数:
one hundred  10
one hundred and one 16
...
四位数:
one thousand 11

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int main()
{
const int HUNDRED = 7;
const int THOUSAND = 8;
const int AND = 3;
const int single_digit[9] = {3, 3, 5, 4, 4, 3, 5, 5, 4};
const int double_digit_1[10] = {3, 6, 6, 8, 8, 7, 7, 9, 8, 8};
const int double_digit_2[8] = {6, 6, 5, 5, 5, 7, 6, 6};
// calc 1 - 9
int single_count = 0;
for(auto e : single_digit)
single_count += e;

// calc 10~19
int double_count = 0;
for(auto e : double_digit_1)
double_count += e;

// calc 20 ~ 99
for(auto e : double_digit_2)
double_count += e * 10 + single_count;

// calc 100 ~ 999
int three_count = 0;
for(auto e : single_digit)
{
three_count += (e + HUNDRED) * 1; // 100 200 300 ...
three_count += (e + HUNDRED + AND) * 9 + single_count; // 101 ... 201 ...
three_count += (e + HUNDRED + AND) * 90 + double_count; // 110 ... 210 ...
}

// calc 1000
int four_count = single_digit[0] + THOUSAND;

cout << single_count + double_count + three_count + four_count << endl;
return 0;
}

答案

21124

[018] Maximum path sum I

题意

给出一个空间结构类似杨辉三角的一些数字,有十五层,最上层有一个数字,最下层有十五个数字。找出从最上层到最下层一条连续的路径,使这条路径上的数字代数和最大。

思路

简单动态规划。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const int MAXN = 100;
int maps[MAXN+1][MAXN+1];
int dp[MAXN+1][MAXN+1];
int max(int a,int b)
{
return (a > b) ? a : b;
}
int main(void)
{
for(int i = 1; i <= MAXN; ++i)
for(int j = 1; j <= i; ++j)
cin >> maps[i][j];
for(int i = MAXN - 1; i >= 1; --i)
{
for(int j = 1; j <= i; ++j)
dp[i][j] = max(dp[i+1][j] + maps[i+1][j], dp[i+1][j+1] + maps[i+1][j+1]);
}
cout << dp[1][1] + maps[1][1] << endl;
return 0;
}

答案

1074

相似题目

Maximum path sum II

[019] Counting Sundays

题意

给出下列信息:

  • 1900.01.01 是星期一
  • 四月、六月、九月、十一月有30天
  • 二月平年有28天,闰年有29天
  • 剩余月份均为31天
  • 闰年定义为:任意年可以被4整除,特殊的,对于世纪年需要被400整除。否则就是平年。

问:20世纪(1901年1月1日到2000年12月31日)一共有多少个星期日落在了当月的第一天。

思路

模拟,对每一年每一月都实际进行检测,统计次数,输出。

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int isLeap(int year, int month)
{
if(month != 1)
return 0;
if(year % 100 == 0)
return (year % 400 == 0) ? 1 : 0;
else
return (year % 4 == 0) ? 1 : 0;
}
int main(void)
{
const int dayPerMonth[12]={31,28,31,30,31,30,31,31,30,31,30,31};
int nowDay = 0;
int ans = 0;
for(int year = 1901; year <= 2000; ++year)
for(int month = 0; month < 12; ++month)
{
if(nowDay == 0)
++ans;
nowDay = (nowDay + dayPerMonth[month] + isLeap(year, month)) % 7;
}
cout << ans << endl;
return 0;
}

拓展

蔡勒公式
观察,公元元年1月5日是星期五,公元二年1月5日是星期六,公元三年1月5日是星期天,也就是说,平年过一年,星期中的天数只增加一,遇闰年则增加2。于是我们可以从公元元年1月1日是星期一这个事实出发,计算需推算的日子离元年1月1日相距多少天(W),再用天数W除以7的余数加上1就是星期几了。即公式如下

$$
W = \lfloor Y-1 \rfloor + \lfloor \frac{Y-1}{4} \rfloor + \lfloor \frac{Y-1}{400} \rfloor + D \\
W为天数,算出来的结果可正可负,对7取模得到星期几 \\
Y为目标年份,D为目标年份下的天数
$$
这样的公式比较麻烦,每个月的天数是不一致的,2月份还有平闰年之分,蔡勒提出了新的最好用的公式:

$$
W = \lfloor \frac{C}{4} \rfloor - 2 \times C + y + \lfloor \frac{y}{4} \rfloor + \lfloor 13 \times \frac{M+1}{5} \rfloor + d - 1 \\
C为目标年份前两位,y为年份后两位 \\
M为目标月份,d为日数,1月与2月要按上一年的13月和14月来算 \\
$$
蔡勒公式只适合于1582年10月15日之后的情形。罗马教皇格里高利十三世在1582年组织了一批天文学家,根据哥白尼日心说计算出来的数据,对儒略历作了修改。将1582年10月5日到14日之间的10天宣布撤销,继10月4日之后为10月15日。

答案

171

[020]Factorial digit sum

题意

$n!$意为$$1\times2\times3\times … \times n$,即n的阶乘。
现在有 $10! = 3628800$ ,然后 $3 + 6 + 2 + 8 + 8 + 0 + 0 = 27$ 。
现在求 $100!$ 的各数位之和。

思路

使用我之前的大数乘法模版,代码略。

答案

648

欧拉计划个人题解(006-010)

[006]Sum square difference

题意

计算$(1 + 2 + … + 100)^2 - (1^2 + 2^2 + … + 100^2)$的结果。

思路

暴力做

优化

$$
(1+2+…+100)^2 = \left( \frac{n \times (n+1)}{2} \right)^2 \\
(1^2 + 2^2 + … + 100^2) = \frac{n \times (n + 1) \times (2n+1)}{6}
$$

代码

C++\STL

1
2
3
4
5
6
7
8
9
int main(void)
{
const int n = 100;
int a = n * (n+1) / 2;
a *= a;
int b = n * (n+1) * (2*n+1) / 6;
cout << a - b << endl;
return 0;
}

答案

25164150

[007]10001st prime

题意

找第10001个质数。

思路

直接筛选,但是判定素数函数有很多优化可以做。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
bool isprime(int n)
{
if(n == 1)
return false;
if(n < 4)
return true;
if(n % 2 == 0)
return false;
if(n < 9)
return true;
if(n % 3 == 0)
return false;
int upper = static_cast<int>(sqrt(n));
for(int i = 5; i <= upper;)
{
if(n % i == 0 || n % (i+2) == 0)
return false;
i += 6;
}
return true;
}

int main(void)
{
int num = 1;
int count = 0;
int limit = 10001;
while(count < limit)
{
++num;
if(isprime(num))
++count;
}
cout << num << endl;
return 0;
}

答案

104743

[008]Largest product in a series

题意

有1000个数字,找其中连续的13个数字,求其最大值。

思路

直接做,注意对0的特殊处理。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
char maps[20 * 51];
int main(void)
{
for(int i = 0; i < 20; ++i)
scanf("%s",maps + (i * 50));
for(int i = 0; i < 20 * 50; ++i)
maps[i] -= '0';
long long maxs = -1LL;
long long tmp = 1LL;

int count = 0;
for(int i = 0; i < 20 * 50; ++i)
{
if(maps[i] == 0)
{
count = 0;
tmp = 1LL;
continue;
}
tmp *= maps[i];
++count;
if(count > 13)
{
tmp /= maps[i-13];
--count;
}
if(count == 13)
{
maxs = max(maxs, tmp);
}
}

cout << maxs << endl;
return 0;
}

答案

23514624000

[009]Special Pythagorean triplet

题意

求符合

$$
\begin{align}
a^2 + b^2 &= c^2 \\
a + b + c &= 1000 \\
a < b < c
\end{align}
$$
的$a \times b \times c$。

思路

易想到,将$c = 1000 - a - b$代换到一式中,然后二重循环扫描得出结果。

于是有下面的代码,

C++\STL

1
2
3
4
5
6
7
8
9
10
11
int main(void)
{
for(int b = 1; b < 1000; ++b)
for(int a = 1; a < b; ++a)
if(1000000 - 2000*(a+b) + 2*a*b == 0)
{
cout << 1LL * a * b * (1000-a-b) << endl;
break;
}
return 0;
}

优化

翻译节选自欧拉计划官网题解1,许多地方有词不达意的情况。

9.2 勾股三角形参数化

一组勾股三角形(a,b,c)当$gcd(a,b,c) = 1$的情况下,被定义为原始/原生的。因为任意勾股三角形有$gcd(a,b) = gcd(b,c) = gcd(c,a)$的性质,而当且仅当$gcd(a,b) = 1$,这样的一个三角形才是原始/原始的。正如古希腊人所知道的,所有原始/原生的勾股三角形可以被表示为
$$
\begin{align}
(9.1) \\
a &= m^2 - n^2 \\
b &= 2 \times m \times n \\
c &= m^2 + n^2 \\
\end{align}
$$
此时$0 < n < m$,也许要交换$a$和$b$使得$a < b$。这些方程总是可以构造出一个勾股三角形,但是当且仅当有确切的一组$m,n$为偶数且$gcd(m,n) = 1$,才会被视作原始/原生的。
从任意一个勾股三角形你可以取得一个原始/原生的三角形,然后除去一个最大公因数,所以每一个勾股三角形有一个独一无二的表示:
$$
\begin{align}
(9.2) \\
a &= (m^2 - n^2) \times d \\
b &= 2 \times m \times n \times d \\
c &= (m^2 + n^2) \times d \\
\end{align}
$$
此时$0 < n < m$,且$gcd(m,n) = 1$,并且有确切的一组$m,n$为偶数,$d$代表$a,b,c$的最大公因数。

将其参数化后,我们可以得到
$$
(9.3) \\
a + b + c = 2 \times m \times (m + n) \times d
$$
所以为了找一个勾股三角形(a,b,c) 且 $a + b + c = s$,我们需要找到一个$\frac{s}{2}$的因子$m (> 1)$ 和一个$\frac{s}{2m}$奇数因子$k (=m+n)$,同时满足$m < k < 2m$和$gcd(m,k) = 1$,然后设$n = k - m$,$d = \frac{s}{2mk}$,联立(9.2),可得出答案。

一个简单恰当的算法实现如下:

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int gcd(int a, int b)
{
return (b != 0) ? gcd(b,a%b) : a;
}
int main(void)
{
int s2 = 1000 / 2;
int mlimit = static_cast<int>( ceil(sqrt(s2)) ) - 1;
for(int m = 2; m <= mlimit; ++m)
if(s2 % m == 0)
{
int sm = s2 / m;
while(sm % 2 == 0)
sm = sm / 2;
int k;
if(m % 2 == 1)
k = m + 2;
else
k = m + 1;
while( k < 2 * m && k <= sm )
{
if(sm % k == 0 && gcd(k, m) == 1)
{
int d = s2 / (k*m);
int n = k - m;
int a = d * (m * m - n * n);
int b = 2 * d * m * n;
int c = d * (m * m + n * n);
printf("%d %d %d %d",a,b,c,a*b*c);
return 0;
}
k += 2;
}
}
return 0;
}

答案

31875000

[010]Summation of primes

题意

计算二百万(2e6)内的所有质数和。

思路

埃式筛。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void sieve(vector<int>& v, const int upper)
{
bool *num = new bool[static_cast<unsigned long long>(upper)];
memset(num, false, sizeof(bool) * upper);
for(int i = 2; i <= upper; ++i)
{
if(num[i] == false)
{
v.push_back(i);
int p = 2;
while(i * p < upper)
{
num[i*p] = true;
++p;
}
}
}
delete[] num;
}
int main(void)
{
vector<int> v;
const int upper = 2e6;
sieve(v,upper+1);
long long sum = 0;
for(auto i : v)
sum += i;
cout << sum << endl;
return 0;
}

答案

142913828922

欧拉计划个人题解(011-015)

[011]Largest product in a grid

题意

在20*20的矩阵中,找4个同方向(横竖斜)的数字,使它们的乘积最大。

思路

直接做,注意对0的处理。

优化

$$
(1+2+…+100)^2 = \left( \frac{n \times (n+1)}{2} \right)^2 \\
(1^2 + 2^2 + … + 100^2) = \frac{n \times (n + 1) \times (2n+1)}{6}
$$

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
int main(void)
{
for(int i = 0; i < 20; ++i)
for(int j = 0; j < 20; ++j)
cin >> maps[i][j];

int ans = 0;
int tmp;

// horizontal
for(int i = 0; i < 20; ++i)
for(int j = 0; j < 20 - 4; ++j)
{
tmp = 1;
for(int k = 0; k < 4; ++k)
tmp *= maps[i][j+k];
ans = max(ans,tmp);
}
// vertical
for(int i = 0; i < 20 - 4; ++i)
for(int j = 0; j < 20; ++j)
{
tmp = 1;
for(int k = 0; k < 4; ++k)
tmp *= maps[i+k][j];
ans = max(ans,tmp);
}

// diagonal1
for(int i = 0; i < 20 - 4; ++i)
for(int j = 0; j < 20 - 4; ++j)
{
tmp = 1;
for(int k = 0; k < 4; ++k)
tmp *= maps[i+k][j+k];
ans = max(ans,tmp);
}
// diagonal2

for(int i = 3; i < 20; ++i)
for(int j = 0; j < 20 - 4; ++j)
{
tmp = 1;
for(int k = 0; k < 4; ++k)
tmp *= maps[i-k][j+k];
ans = max(ans,tmp);
}

cout << ans << endl;
return 0;
}

答案

70600674

[012]Highly divisible triangular number

题意

三角形数序列是由对自然数的连加构造而成的。所以第七个三角形数是$1 + 2 + 3 + 4 + 5 + 6 + 7 = 28$。那么三角形数序列中的前十个是:$1, 3, 6, 10, 15, 21, 28, 36, 45, 55, …$
下面我们列出前七个三角形数的约数:

1
2
3
4
5
6
7
1: 1
3: 1,3
6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28

可以看出28是第一个拥有超过5个约数的三角形数。
那么第一个拥有超过500个约数的三角形数是多少?

思路

一个根据题意找,注意可以开平方缩小范围。

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool isaccepted(long long num)
{
int factor_count = 0;
long long upper = static_cast<long long>(sqrt(num));
for(int i = 2; i < upper; ++i)
if(num % i == 0)
factor_count += 2;
if(upper * upper == num)
factor_count += 1;
if(factor_count >= 500)
return true;
else
return false;
}
int main(void)
{
int ans = 1;
int i = 1;
while(!isaccepted(ans))
ans += ++i;
cout << ans << endl;
return 0;
}

优化

但是上面的代码还是很慢,我们需要引入一个新的算法。

我们知道,一个数的因数,可以由一些质因数组合而成,那么一个数的因数个数与质因数会有关系,我们把一个数分解成若干个质因数,例如

$$
28 = 2^2 * 7^1
$$
则其组合的情况有

$$
2^0 \times 7^0 \\
2^1 \times 7^0 \\
2^2 \times 7^0 \\
2^0 \times 7^1 \\
2^1 \times 7^1 \\
2^2 \times 7^1 \\
$$
共计6种,其实就是$6 = 3 * 2 = (2 + 1) \times (1 + 1)$
即为各质因子指数加一的乘积,但剩下的算法有些看不懂,留待更新。1

答案

76576500

[013]Large sum

题意

给出一百个50位数字的数,计算出他们的和,结果只取前十位数。

思路

高精度加法。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
class BigInt
{
public:
const size_t SIZE = 60;

BigInt();
~BigInt();
BigInt(int num);
BigInt(BigInt&& b);
BigInt(const BigInt& b);
BigInt& operator = (const BigInt&);
bool operator == (const BigInt& b);
void operator += (const BigInt& b);
string toString();
friend istream& operator >> (istream& in, BigInt& b)
{
string str;
in >> str;
auto p = str.rbegin();
size_t postion = 0;
while(p != str.rend())
{
b.m_num[postion] = (*p) - '0';
++postion;
++p;
}
return in;
}
friend ostream& operator <<(ostream& out, const BigInt& b)
{
size_t postion = b.SIZE - 1;
while(b.m_num[postion] == 0 && postion != 0)
--postion;
while(postion != 0)
{
out << static_cast<int>(b.m_num[postion]);
--postion;
}
out << static_cast<int>(b.m_num[0]);
return out;
}
private:
char* m_num;
};

BigInt::BigInt()
{
m_num = new char[SIZE];
memset(m_num, 0, sizeof(char) * SIZE);
}

BigInt::~BigInt()
{
if(m_num != nullptr)
delete[] m_num;
}
BigInt::BigInt(int num)
{
assert(num >= 0);
m_num = new char[SIZE];
memset(m_num, 0, sizeof(char) * SIZE);
int p = 0;
do{
m_num[p] = num % 10;
num /= 10;
p++;
}while(num != 0);
}
BigInt::BigInt(BigInt&& b)
{
m_num = b.m_num;
b.m_num = nullptr;
}
BigInt::BigInt(const BigInt& b)
{
this->m_num = b.m_num;
}
bool
BigInt::operator==(const BigInt& b)
{
for(size_t i = 0;i < SIZE; ++i)
if(m_num[i] != b.m_num[i])
return false;
return true;
}
void
BigInt::operator+=(const BigInt& b)
{
for(size_t i = 0; i < SIZE; ++i)
m_num[i] += b.m_num[i];
for(size_t i = 0; i < SIZE - 1; ++i)
{
m_num[i + 1] += m_num[i] / 10;
m_num[i] = m_num[i] % 10;
}
}
string
BigInt::toString()
{
size_t postion = SIZE - 1;
while(m_num[postion] == 0 && postion != 0)
--postion;
string str(postion + 1, '\0');
auto p = str.begin();
while(postion != 0)
{
(*p) = m_num[postion] + '0';
--postion;
++p;
}
(*p) = m_num[postion] + '0';
return str;
}
int main(void)
{
BigInt sum,tmp;
while(cin >> tmp)
sum += tmp;
cout << sum.toString().substr(0,10) << endl;
return 0;
}

答案

5537376230

[014]Longest Collatz sequence

题意

3n+1猜想。猜想的内容如下:
对于一个正整数n,若其为偶数,则将其除2,若其为奇数,则将其乘三再加一,重复上述操作,直至这个数变为1。
题目问一百万(1e6)内,哪一个数需要进行的操作数最多,输出这个数。

思路

模拟,对每一个数都实际进行若干次操作,统计次数,输出。

优化

因为对于一个数来说,他操作的过程中,会演化成其他数,这些数字的次数同时也可以确认下来,那么我们就节约了计算这些数字的时间,就是一个记录的过程。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const int MAXN = 1e6;
int num[MAXN+2];
int main(void)
{
memset(num,0,sizeof(num));
num[1] = 1;
int ans = 1;
stack<long long> s;
for(int i = 1; i <= MAXN; ++i)
{
if(num[i] == 0)
{
s.push(i);
long long tmp = i;
int count;
while(tmp != 1)
{
tmp = (tmp % 2) ? (3 * tmp + 1) : (tmp / 2);
if(tmp <= MAXN && num[tmp] != 0)
{
count = num[tmp];
while(!s.empty())
{
++count;
if(s.top() <= MAXN)
num[s.top()] = count;
s.pop();
}
break;
}
s.push(tmp);
}
}
if(num[ans] < num[i])
ans = i;
}
cout << ans << endl;
return 0;
}

答案

837799

[015]Lattice paths

题意

在一个方格内,从左上角,沿着线,可以选择向右走一段或向下走一段,直至右下角,计算不重复的路径数量。对于一个22的方格,答案是6种,现在问2020的方格有多少种可能。

思路

DP,之前想成了卡特兰数,细看发现规则不一样,于是有下面的代码。

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const int MAXN = 21;
unsigned long long dp[MAXN][MAXN];
int main(void)
{
dp[0][0] = 1;
// init
for(int i = 1; i < MAXN; ++i)
{
dp[i][0] = dp[i-1][0];
dp[0][i] = dp[0][i-1];
}
for(int i = 1; i < MAXN; ++i)
for(int j = 1; j < MAXN; ++j)
dp[i][j] = dp[i-1][j] + dp[i][j-1];
cout << dp[MAXN-1][MAXN-1] << endl;

return 0;
}

优化

其实这是一个简单的排列组合问题,路径的数量其实就是$C^{20}_{40}$
然后会爆空间,还可以做简化。

$$
\begin{align}
C^{20}_{40} &= \frac{40 \times 39 \times 38 … \times 21}{20 \times 19 \times 18 … \times 1} \\
&= \frac{39 \times 37 \times 35 … \times 21}{10 \times 9 \times 8 … \times 1} \times 2^{10}
\end{align}
$$
代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main(void)
{
long long mo = 1LL;
long long de = 1LL;
for(int i = 1; i <= 10; ++i)
{
mo *= 19 + (i * 2);
de *= i;
long long tmp = gcd(mo,de);
mo /= tmp;
de /= tmp;
}
long long ans = mo * 1024 / de;
cout << ans;
return 0;

}

答案

137846528820

欧拉计划个人题解(001-005)

[001]Multiples of 3 and 5

题意

找寻[1,1000)以内,是3的倍数,或是5的倍数的数。

思路

暴力搜索判定

优化

容斥,先计算3的倍数在1000内有多少个,然后用等差公式计算出1000内3的倍数的和,同理有5的倍数,两者相加,重复的数用15的倍数的和减去即可。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
long long SumOf(long long up, int k)
{
long long cnt = (up - 1) / k;
return (k + cnt * k) * cnt / 2;
}
int main()
{
long long n;
cin >> n;
cout << SumOf(n, 5) + SumOf(n, 7) - SumOf(n, 35) << endl;
return 0;
}

答案

233168

[002]Even Fibonacci numbers

题意

斐波那契数列:

1
2
3
4
F[1] = 1
F[2] = 1
F[3] = 2
...

求斐波那契数列中数字为偶数的所有数字之和,数字不大于400万。

思路

递推暴力

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
int ans = 0;
int a = 1, b = 1, c = 2;

while(c <= 4000000)
{
ans += c;
a = b + c;
b = a + c;
c = a + b;
}

cout << ans << endl;
}

优化

列表找规律:

1
2
3
4
5
6
7
8
9
F[1] =  1     F[2] = 1
F[3] = 2 F[4] = 3
F[5] = 5 F[6] = 8
F[7] = 13 F[8] = 21
F[9] = 34 F[10] = 55
F[11] = 89 F[12] = 144
F[13] = 233 F[14] = 377
F[15] = 610 F[16] = 987
F[17] = 1597 F[18] = 2584

符合题意的有

1
2
3
4
5
6
F[3] = 2
F[6] = 8
F[9] = 34
F[12] = 144
F[11] = 610
F[18] = 2584

观察可得 F[n] = 4 * F[n-3] + F[n - 6]

正确性可以由斐波那契数列定义推出:

$$
\begin{align}
F_n &= F_{n-1} + F_{n-2} \\
& = F_{n-2} + F_{n-3} + F_{n-2} = 2 \times F_{n-2} + F_{n-3} \\
& = 2 \times ( F_{n-3} + F_{n-4} ) + F_{n-3} ) = 3 \times F_{n-3} + 2 \times F_{n-4} \\
& = 3 \times F_{n-3} + F_{n-4} + F_{n-5} + F_{n-6} \\
& = 4 \times F_{n-3} + F_{n-6} \\
\end{align}
$$
代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
int a[3] = {2,8,34};
int ans = 0;
int p = 2;
while(a[p] < 4000000)
{
ans += a[p];
p = (p + 1) % 3;
a[p] = 4 * a[(p + 2) % 3] + a[(p + 1) % 3];
}
cout << ans << endl;
}

答案

4613732

[003]Largest prime factor

题意

求 600851475143 最大的质因子

思路

刚开始先暴力的,发现循环判定再循环判定复杂度就太高了。于是用埃氏筛法扫过去就行了。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int Eratosthenes()
{
const long long TARGET = 600851475143LL;
const int upper = static_cast<const int>(sqrt(TARGET));
bool *num = new bool[static_cast<unsigned long long>(upper)];
memset(num, false, sizeof(bool) * static_cast<unsigned long long>(upper));
int ans = 2;
for(int i = ans; i < upper; ++i)
{
if(num[i] == false)
{
int p = 2;
while(i * p < upper)
{
num[ i * p ] = true;
++p;
}
if(TARGET % i == 0)
ans = i;
}
}
delete[] num;
return ans;
}

int main(void)
{
int ans = Eratosthenes();
cout << ans << endl;
return 0;
}

答案

6857

[004]Largest palindrome product

题意

对于一个回文数,如:9009 = 91 × 99,他是由两个两位数构成的。现在找两个三位数能构成的最大回文数。

思路

首先是构造回文数,它符合一下几个规律:

  1. 六位数
  2. 六位数里面最大的
  3. 前三位和后三位对应相同

由上,我们将回文数按位拆分,枚举前一二三位,做对应就行了。值得一提的是第一位不可能为0,二三位都有可能为0。

优化

回文数必然可以被11整除,证明如下: 回文数的形式必然为:

100000a + 10000b + 1000c + 100c + 10b + a
=100001a + 10010b + 1100c
因为:
100001 % 11 = 0
10010 % 11 = 0
1100 % 11 = 0
所以:
回文数必然可以被11整除

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main()
{
int num;
int mark = 1;
for(int i = 9; i >= 1 && mark; --i)
for(int j = 9; j >= 0 && mark; --j)
for(int k = 9; k >= 0 && mark; --k)
{
num = i*100 + j * 10 + k;
num = num * 1000 + k * 100 + j * 10 + i;
for(int t = 999; t >= 100 && mark; --t)
if(num % t == 0 && num / t >= 100 && num / t < 1000)
{
cout << num << endl;
mark = 0;
}
}
return 0;
}

答案

906609

[005]Smallest multiple

题意

2520是最小的能被1-10中每个数字整除的正整数。 最小的能被1-20中每个数整除的正整数是多少?

思路

刚开始想直接找到20以内的质数2,3,5,7…相乘得出答案的,后来交了发现不对,尝试了一次,发现在4的时候就不能整除了。后来百度搜了一下,要:

  1. 列出20以内的质数,并求这些质数的小于20的最高次幂
  2. 再将这些质数的最高次幂的积相乘

于是有了下面的代码。

代码

C++\STL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool check(vector<int>& s, int num)
{
for(int e : s)
if(num % e == 0)
return false;
return true;
}
int main(void)
{
vector<int> s;
for(int i = 2; i <= 20; ++i)
if(check(s,i))
s.push_back(i);
long long ans = 1LL;
for(int e : s)
{
int m = 1;
while(m * e <= 20)
m *= e;
ans *= m;
}
cout << ans << endl;
return 0;
}

答案

232792560

Python3 学习之路(5) 列表、元组与字典

列表

可以理解为列表为可变的数组,不过Python中的列表较之灵活许多。

访问改动

使用下标索引来访问并改动列表中的值,同样你也可以使用方括号的形式截取字符,访问并改动,超出范围则会报错。

index(obj)

返回目标列表中obj出现的位置,如果没有则抛出异常。

增减

append(obj)

向目标列表的最后添加一个元素。

extend(seq)

向目标列表的最后添加如干个元素,seq要是一个可迭代的参数。

insert(index,obj)

向目标列表指定位置插入一个元素。

del

可以使用 del 语句来删除列表的的元素,可以删除整个列表,也可以通过下标索引来删除列表中的某个或某些元素。

1
2
3
4
5
6
7
list = ['A','B','C','D','E','F']

del list[0] # 删除一个元素
print(list) # 输出['B', 'C', 'D', 'E', 'F']
del list[1:3] # 删除一些元素
print(list) # 输出['B', 'E', 'F']
del list # 删除整个列表

remove(obj)

移除目标列表的指定元素,找不到则抛出异常。

pop([index=-1])

移除列表中的一个元素(默认最后一个元素),并且返回该元素的值,没有值可以返回则抛出异常。

操作符

列表的操作与字符串运算符的种类和效果基本相同,此处不表,请参照之前的文章。

其他函数

len(list)

返回列表元素个数。

clear()

清除目标列表的所有元素。

max(list) min(list)

返回列表元素的最大/小值,如果列表中的元素不是一种类型或者没有重载小于号,会报错。
当列表中元素均是一种类型,可以通过此类型的默认排序获得结果。

1
2
list = [[1,2],[4,4],[4,2,2]]
print(max(list)) # 输出[4,4] 即先比较第一个数,再比较第二个数

sort(cmp=None, key=None, reverse=False)

对原列表进行排序,不返回值。

  • cmp 可选参数, 如果指定了该参数会使用该参数的方法进行排序。
  • key 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
  • reverse 排序规则,reverse = True 降序, reverse = False 升序(默认)。

sort还可以搭配类中重载小于号的方式来使用,此处按下不表。

元组

可以理解为元组为不可变的数组,目的是为了代码的安全性。

访问

使用下标索引来访问元组中的值。

改动

元组中的元素值是不允许修改的,但我们可以对元组进行连接组合。

删除

元组中的元素值是不允许删除的,但我们可以使用del语句来删除整个元组。

操作符

元组的操作与字符串运算符的种类和效果基本相同,此处不表,请参照之前的文章。

其他函数

元组同列表一样拥有len()、min()、max()等函数。

字典

字典同其他主流编程语言的map一样,是一种key->value的容器,查询速度很快。
要注意的是,key要是唯一的且不可变,但value不一定。如果重复对一个key进行改动,则会把旧的抛弃,更新为新的。

访问

使用下标索引(key)来访问字典中的值。没有会报错。

改动

使用下标索引(key)来更新元组中的值。

删除

有三种方法可以不同程度上的删除字典。

del dict[]

删除字典里的一对key->value的元素。

clear()

删除字典里面的所有元素。

del dict

删除整个字典。dsadsadsadsa

其他函数

len(dict)

计算字典元素个数,即键的总数。

str(dict)

输出字典,以可打印的字符串表示。

fromkeys()

创建一个新字典,以序列seq中元素做字典的键,val为字典所有键对应的初始值。

get(key, default=None)

返回指定键的值,如果值不在字典中返回default值。

items()

以列表返回可遍历的(键, 值) 元组数组。

setdefault(key, default=None)

和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default。

pop(key[,default])

删除字典给定键 key 所对应的值,返回值为被删除的值。key值必须给出。 否则,返回default值。

keys()/values()

以列表返回一个字典所有的键/值。


引用

  1. http://www.runoob.com/python3/python3-list.html
  2. http://www.runoob.com/python3/python3-tuple.html
  3. http://www.runoob.com/python3/python3-dictionary.html
  4. https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014316724772904521142196b74a3f8abf93d8e97c6ee6000
  5. https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143167793538255adf33371774853a0ef943280573f4d000