分治算法

2022-06-16  本文已影响0人  Python_Camp

描述。
有8个球,编号从0到7,其中7个球的重量相同。有一个更重。你的任务是找到它的号码。

你的函数findBall将收到一个参数--scales对象。天平对象包含一个内部存储的8个元素的数组(索引为0-7),每个元素都有相同的值,只有一个元素更大。它还有一个名为getWeight(left, right)的公共方法,该方法接收两个索引数组,并根据在所传递的索引处发现的数值的累加,返回-1、0或1,这些数值是较重、相等或较轻的。

getWeight返回。

如果左盘较重则返回-1

如果右锅较重,则返回1

如果两个平底锅的重量相同,则返回0

scales.getWeight()的用法示例。

scales.getWeight([3], [7]) 如果球3比球7重,则返回-1;如果球7更重,则返回1;如果这些球的重量相同,则返回0。

scales.getWeight([3, 4], [5, 2]) 如果球3和4的重量比球5和2的重量重,返回-1。

那么,你可能会问,问题出在哪里呢?好吧--这个天平非常古老。在天平损坏之前,你只能使用它两次。

注意 - 在Python、Crystal和Ruby版本中使用scales.get_weight()。

def find_ball(scales):
    part = [[None, 0, 1], [2, 3, 4], [5, 6, 7]]
    res1 = scales.get_weight(part[-1], part[1])
    res2 = scales.get_weight([part[res1][-1]], [part[res1][1]])
    return part[res1][res2]

难度升级

有n个球,编号从0到n-1(0,1,2,3,等等)。其中大部分球的重量相同,但有一个球比较重。你的任务是找到它。

你的函数将收到两个参数--一个天平对象,和一个球的数量。天平对象只有一个方法。

get_weight(left, right)

其中左和右是分别放在左盘和右盘的球的数量数组。

如果该方法返回-1,则左盘较重。
如果该方法返回1,则右盘更重。
如果该方法返回0,则两个锅的重量相同。

那么,是什么让这个卡塔的 "全能大师 "版本?
首先,它不像以前的版本那样只限于8个球--你的解决方案必须适用于8-500个球。
第二,使用天平的次数不能超过图表中的列表。

小球数量 | 次数
ball count | uses


       0-9 |    2
   10-27 |    3
  28-81  |    4
 82-243 |    5

244-500 | 6

def find_ball(scales, n):
    select = list(range(n))
    while len(select) > 1:
        left, right, unused = select[::3], select[1::3], select[2::3]
        if len(select) % 3 == 1: unused.append(left.pop())
        select = [left, unused, right][scales.get_weight(left, right) + 1]
    return select.pop()

这是高手!

分金子的奥秘

在野外,两个程序员A和B同时发现了一些黄金。他们都想得到这些金子,于是他们决定用以下规则来分配金子。

他们把金子分成n堆,并排在一起。每堆的数量和堆的顺序都是随机的。

他们轮流从最左边或最右边取走一堆黄金。

在每一轮中,程序员将用他的智慧来选择对他最有利的黄金。

输入
一个代表黄金的整数的列表*。

输出
计算出的A和B最终获得的黄金数量,以一个元组*(A的数量,B的数量)。

*列表/元组的表示方法因语言而异--见实例测试

注意,我们可以假设A总是先拿,而且每次都使用最佳策略。

例子
对于黄金=[10,1000,2,1],输出应该是[1001,12]。

在第一轮,A可以拿10或1,10比1大。
我们应该选择10吗?不,聪明的程序员不这么认为;-)
因为如果A选择10,下一回合B可以选择1000。
所以,A选择1是最好的选择。

在第二轮,无论B选择10还是2,在第三轮,A总是可以选择1000。
所以,B选择10是最好的选择。

最终结果。
A: 1 + 1000 = 1001
B: 10 + 2 = 12
对于金币=[10,1000,2],输出应该是[12,1000]。

在这个例子中,A面临的选择与前一个例子中的第2个回合相同。

性能算法

代码块

杜德数据挑战赛 3 - 最佳解决方案

在这篇文章中,我将介绍杜德数据挑战赛第3期的 "最佳 "解决方案。关于问题的设置,请参考那篇文章。

天真的解决方案--带应用的自定义函数
在上一篇文章中详细介绍了天真的解决方案。最终的结果是一个庞大的自定义函数,包含许多布尔过滤器,用于寻找特定的数据子集进行聚合。对于每一个组,都会返回一个有11个值的系列。这些值中的每一个都成为结果数据框架中的一个新列。让我们来看看这个自定义函数。

Dunder Data Challenge3 Optimal Solution.png

我们使用这个天真的解决方案的性能需要近4秒。

成为一个专家
如果你想让别人信任你使用pandas做决策,你必须成为专家。我已经完全掌握了pandas,并开发了一些课程和练习,将大量提高你的知识和效率来做数据分析。

用Python掌握数据分析 - 我的综合课程有800多页,350多个练习,多个项目,以及详细的解决方案,将帮助你成为pandas的专家。
通过报名参加免费的Pandas入门课程,获得材料的样本。
最优解决方案
为了大大增加我们的性能,我们需要利用groupby对象的内置方法。上面,我们使用了一个自定义函数来做很多很多的计算。这些计算是在每个组上进行的。让我们得到组的总数。

>>> len(df.groupby(['country', 'region']))
520

len(df.groupby(['国家', '地区']))
520
对于这520个组中的每一个,都调用了大量的f_final函数,重新计算了每个过滤器。为每个组运行这些相同的计算是导致apply性能不佳的主要原因之一。

看一下f_final中的所有过滤器。你会注意到,它们中的每一个都是独立于特定组的。这意味着我们可以在分组之前计算这些过滤器并得到相同的结果。作为一个具体的例子,看一下下面的DataFrame。

df_rev = pd.DataFrame({'state': ['TX', 'TX', 'TX', 'CA', 'CA', 
                                 'CA'], 
                       'category': ['tech', 'energy', 'energy', 
                                    'tech', 'energy', 'energy'],
                       'revenue' : [10, 5, 8, 20, 12, 2]})

df_rev = pd.DataFrame({'state': ['TX', 'TX', 'TX', 'CA', 'CA',
'CA']。
'类别': ['科技', '能源', '能源',
'科技', '能源', '能源'],
'收入':[10, 5, 8, 20, 12, 2]})


Dunder Data Challenge3 Optimal Solution2.png

让我们来计算每个州的收入,但只是能源类别的收入。一个天真的解决方案是编写一个自定义函数,每个组将被过滤掉能源类别,然后对收入进行求和。

def f(x):
    is_energy = x['category'] == 'energy'.
    return x.loc[is_energy, 'revenue'].sum()

使用这个自定义函数和apply返回每个州的能源类别的正确收入。

>>> df_rev.groupby('state').apply(f)
state
CA    14
TX    13


加州 14
德克萨斯州 13

相反,我们可以为能源收入创建一个完整的新列。首先,我们创建一个布尔系列,其中True对应于 "能源"。我们把这个系列乘以原来的收入列。因为False的值是0,True的值是1,所以新的列将和原来的列一样,但在类别不是能源的地方有0。

filt = df_rev['category'] == 'energy'
df_rev['energy_revenue'] = filt * df_rev['revenue']
df_rev
Dunder Data Challenge3 Optimal Solution4.png

现在我们可以在分组过程中使用内置的sum方法而不是我们的自定义函数。这里不需要应用。

>>> df_rev.groupby('state')['energy_revenue'].sum()
state
CA    14
TX    13


加州 14
德克萨斯州 13

使用where方法
你可能需要将过滤后的数值替换为0,而不是像我们上面所做的那样,将它们变成缺失值。如果你要计算像平均数或中位数这样的东西,这是至关重要的,因为它将考虑到0的值。where方法将把传递的布尔系列的假值替换为NaN。

filt = df_rev['category'] == 'energy'
df_rev['energy_revenue'] = df_rev['revenue'].where(filt)
df_rev
Dunder Data Challenge3 Optimal Solution4-1.png

我们可以调用相同的groupby来获得相同的结果。

df_rev.groupby('state')['energy_revenue'].sum()
state
CA    14.0
TX    13.0


加州 14.0
德克萨斯州 13.0

用我们的挑战数据进行过滤
让我们计算一下rev_2019,它被定义为2019年上半年的收入。让我们先用天真的思维方式,定义一个自定义函数。

>>> def get_rev_2019(x):
        is_2019H1 = x['date'].between('2019-01-01', '2019-06-30')
        return x.loc[is_2019H1, 'revenue'].sum()
>>> df.groupby(['country', 'region']).apply(get_rev_2019).head()
country    region
Argentina  A         150508
           B         139048
           C         118035
           D         131728
           E         146201

df.groupby(['国家', '地区']).apply(get_rev_2019).head()

代码块

国家地区
阿根廷 A 150508
B 139048
C 118035
D 131728
E 146201

现在,让我们使用我们的新方法,首先将过滤器应用于整个DataFrame,创建一个新列,然后使用内置的sum方法。

>>> is_2019H1 = df['date'].between('2019-01-01', '2019-06-30')
>>> df['rev_2019'] =  df['revenue'].where(is_2019H1)
>>> df.groupby(['country', 'region'])['rev_2019'].sum().head()
country    region
Argentina  A         150508.0
           B         139048.0
           C         118035.0
           D         131728.0
           E         146201.0

自定义函数与内置方法的性能比较
让我们比较一下这两种方法的性能。

>>> %timeit df.groupby(['country', 'region']).apply(get_rev_2019)
633 ms ± 15.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>>> %%timeit
>>> is_2019H1 = df['date'].between('2019-01-01', '2019-06-30')
>>> df['rev_2019'] = df['revenue'].where(is_2019H1)
>>> df.groupby(['country', 'region'])['rev_2019'].sum()
27.3 ms ± 530 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit df.groupby(['国家', '地区']).apply(get_rev_2019)
633毫秒±15.9毫秒/循环(7次运行的平均值±std.dev.,每次1个循环)。
%timeit
is_2019H1 = df['date'].between('2019-01-01', '2019-06-30')
df['rev_2019'] = df['revenue'].where(is_2019H1)
df.groupby(['国家', '地区']) ['rev_2019'].sum()

每循环27.3毫秒±530微秒(7次运行的平均值±std.dev.,每次10循环)
自定义函数比内置方法慢25倍,这只是一个简单的计算。自定义函数越复杂,性能差异就越大。

避免使用apply的技巧
如果你试图避免使用apply,那么你没有选择,只能使用内置的groupby方法。这就限制了可能性,迫使你以不同的方式来处理这个问题。主要的 "技巧 "是在使用groupby方法之前对整个DataFrame执行操作。

不是所有的操作都能在整个DataFrame上执行,只有那些独立于分组的操作。那么,你怎么知道一个操作是否独立于组?该操作不会有特定于当前组的计算。例如,我们正在按国家和地区进行分组。如果一个操作依赖于特定的国家或地区,那么它将不能在整个DataFrame上执行。

一个具体的例子可以在这里提供帮助--如果上半年的定义是希腊从1月到7月,其他国家从1月到6月,那么上半年的收入计算将取决于组。

在这个挑战中,所有的业务都是独立于集团的。不存在基于集团的特殊情况。这意味着我们可以在分组之前,执行我们在自定义函数内使用的所有操作,以在其之外应用。

完整的最优解决方案
现在将给出完整的最优解。我们将为我们的过滤器使用与自定义函数中相同的定义,但改为在整个DataFrame上计算它们。然后我们将创建新的列,在过滤器为False的地方有NaN。

Dunder Data Challenge3 Optimal Solution5.png

现在我们可以只使用内置的groupby方法来聚合数据。

这并不是最终的DataFrame,因为有些列只能从聚合值的结果中计算出来。我们还需要放弃其中一些在结果中不再需要的中间列。

Dunder Data Challenge3 Optimal Solution6.png

让我们来验证一下,这些DataFrame是等价的。

>>> df1 = df.groupby(['country', 'region']).apply(f_final)
>>> df1.equals(df_new1.astype('float'))
True

让我们把最优解的所有步骤放到一个单一的函数中,然后我们可以用它来衡量性能。

def optimal():
    ... all steps

约20倍的速度
由于在整个DataFrame上预先计算新的列,并且只使用内置的groupby方法,最优方案的速度是天真的方案的20倍左右。

>>> %timeit optimal()

201毫秒±2.48毫秒/循环(7次运行的平均值±std.dev.,每次1个循环)。

关于如何取消apply的总结
我建议不惜一切代价避免apply,因为使用它可能会严重影响性能。下面的步骤总结了避免apply的程序。

在分组之前,计算任何独立于每个组的计算结果。这些计算会被应用到整个DataFrame中,作为一个整体
在你的DataFrame中创建新的列,包含步骤1中这些新计算的结果。
分组时,只使用内置的groupby聚合方法。不要使用自定义函数。
你可能会有一些使用groupby聚合结果的聚合(例如,取平均值和中位数的差值)。在groupby之后计算这些列。

掌握Python、数据科学和机器学习
沉浸在我用Python掌握数据科学和机器学习的全面路径中。购买全能通行证,就可以终身享受所有当前和未来的课程。它包含的一些课程。

银行客服的流程挑刺

1、对不起,您换汇需要联系储蓄卡客服,转接过去,
2、储蓄卡客服问需要查看我有没有储蓄卡,
答:可能办过,但没怎么用,也记不得卡号了,但我是贵行多年的白金卡信用卡 客户,客户资料应该比较全,可否再查下
自从,开启迷之对话 ——

3、对不起,先生您查看名下的储蓄卡,也需要开通电话银行
4、Okay,转自动语音,尝试开通电话银行,按提示:
5、输入卡号,身份证号,提示卡号或者密码不对,有点晕
6、平复心情,泡杯茶败火,一百个小心继续输,第8遍放弃
7、再联系客服,只问一个问题:本人贵行预留的手机号码是多少
8、客服回:请先注册电话银行业务
9、我问不注册可以查吗?曾在柜台办过人脸识别或回答个人私密信息
10、不行,转上一级经理,与经理的对话又回到了起点
11、 又提示我回到电话银行注册
问候内心飘过的一万个草原小骆驼,深吸一口气
12、一字一顿给经理讲:“ 给您免费支个招,贵行发一条营销短信,看到哪部手机收到,就是预留的手机号吗?
13、呵呵呵,对面的经理一阵凌乱:啊这,谢谢您

class User:
    def __int__(self,name,id,phone):
        self.name = name
        self.id = id
        self.phone = phone

    def check_id(self):
        NAME = False
        ID = False
        name = input('您的姓名:')
        if name == self.name:
            print(f"客户的姓名正确")
            NAME = True
        else:
            id = input('您的身份证号:')
            if self.id == id:
                ID = True
                print(f'身份证号码正确!')
                print(f"您现在可以查询的信息:\n"
                      "1.查询卡号"
                      "2.查询预留手机号码"
                      "3.查询还款")
        return NAME,ID

    def checkDebtCard(self)->int: #身份证或者是卡号
        if self.check_id():
            print("您输入密码即可查询还款")
        else:
            return print("您提供的个人信息不正确")

    def smstoPhone(self,phone):
        phoneNo = input('请输入手机号:')
        if self.phone == phoneNo:
            return print('已经发送到您的手机')

# 银行录入用户的信息
zhangmin = User()
zhangmin.name = 'zhangmin'
zhangmin.id = '1234'
zhangmin.phone = '18983086450'

#客户张敏打客服电话
zhangmin.check_id()
zhangmin.checkDebtCard()
上一篇 下一篇

猜你喜欢

热点阅读