背包问题的基本模型是:
有一个容量为C的背包,现在要从N件物品中选取若干件装入背包中,每件物品i的重量为W[i]、价值为P[i]。定义一种可行的背包装载为:背包中物品的总重不能超过背包的容量,并且一件物品要么全部选取、要么不选取。定义最佳装载是指所装入的物品价值最高,并且是可行的背包装载。
例如,设C= 12,N=4,W[4]={2,4,6,7},P[4]={ 6,10,12,13},则装入W[1]和W[3],最大价值为23。
若采用贪心法来解决0/1背包问题,可能选择的贪心策略一般有3种。每种贪心策略都是采用多步过程来完成背包的装入,在每一步中,都是利用某种贪心准则来选择将某一件物品装入背包。
(1)选取价值最大者。
贪心策略为:每次从剩余的物品中,选择可以装入背包的价值最大的物品装入背包。这种策略不能保证得到最优解。例如,设C=30,有3个物品A、B、C,W[3]={28,12,12},P[3]={30,20,20}。根据策略,首先选取物品A,接下来就无法再选取了,此时最大价值为30。但是,选取装B和C,最大价值为40,显然更好。
(2)选取重量最小者。
贪心策略为:从剩下的物品中,选择可以装入背包的重量最小的物品装入背包。其想法是通过多装物品来获得最大价值。这种策略同样不能保证得到最优解。例如,设C=30,有3个物品A、B、C,W[3]={13,14,15},P[3]={20,30,40}。根据策略,首先选取物品A,接下来选取B,之后就无法再选取了,此时最大价值为50。但是,选取装B和C,最大价值为70,显然更好。
(3)选取单位重量价值最大者
贪心策略为:从剩余物品中,选择可装入背包的P[i]/W[i]值最大的物品装入。这种策略还是不能保证得到最优解。例如,设C=40,有3个物品A、B、C,W[3]={15,20,28},P[3]={15,20,30}。按照策略,首先选取物品C(p[2]/w[2]>1),接下来就无法再选取了,此时最大价值为30。但是,选取装A和B,最大价值为35,显然更好。
由上面的分析可知,采用贪心法并不一定可以求得最优解。
背包问题用贪心和搜索求解的效果不佳,其标准的解法是动态规划。
按每一件物品装包为一个阶段,共分为n个阶段。

(1)建立递推关系
设f[i][j]为背包容量j,可取物品范围为i、i+1、…、n的最大效益值。例如,f[1][c]的含义是容量为c的背包、可在1~n件物品中选择物品装入背包后所得的最大效益值。
当0≤j<w[i] 时,物品i不可能装入。最大效益值与f[i+1][j] 相同。
当j≥w[i] 时,有两个选择:
1)不装入物品i,这时最大效益值为f[i+1][j] ;
2)装入物品i,这时已产生效益p[i],背包剩余容量 j−w[i],可以选择物品i+1、…、n来装,最大效益值为f[i+1][j−w[i]] + p[i]。
期望的最大效益值是两者中的最大者。于是递推关系(或称状态转移方程)如下:

其中w[i]、p[i] 均为正整数,i=1、2、…、n。
边界条件为: f[n][j]=p[n] 当j≥w[n] 时 (最后1件物品可装包) ;
f[n][j]=0 当 j<w[n] 时 (最后1件物品不能装包)。
所求最大效益即最优值为f[1][c]。
(2)逆推计算最优值
for (j=0;j<=c;j++) // 首先计算边界条件f[n][j]
if (j>=w[n])
f[n][j]=p[n];
else
f[n][j]=0;
for(i=n-1;i>=1;i--) // 逆推计算f[i][j] (i从n-1到1)
for(j=0;j<=c;j++)
if (j>=w[i] && f[i+1][j]<f[i+1][j-w[i]]+p[i])
f[i][j]= f[i+1][j-w[i]]+p[i];
else
f[i][j]=f[i+1][j];
printf("最优值为%d\n",f[1][c]);
(3)构造最优解
若f[i][cw] > f[i+1][cw] ( i=1、2、…、n−1, cw的初始值为c)
则x[i]=1; 装载w[i], cw=cw−x[i]*w[i]。
否则,x[i]=0,不装载w[i]。
最后,所装载的物品效益之和与最优值比较,决定w[n]是否装载。
#include <stdio.h>
#define MAXN 500
#define MAXC 50000
int f[MAXN][MAXC];
int main()
{
int p[MAXN],w[MAXN];
int n,c;
printf("请输入物品的个数 N:");
scanf("%d",&n);
printf("请输入背包容量 C:");
scanf("%d",&c);
printf("请依次输入每种物品的重量:");
int i,j;
for (i=1;i<=n;i++)
scanf("%d",&w[i]);
printf("请依次输入每种物品的价值:");
for (i=1;i<=n;i++)
scanf("%d",&p[i]);
for (j=0;j<=c;j++) // 首先计算边界条件f[n][j]
if (j>=w[n])
f[n][j]=p[n];
else
f[n][j]=0;
for (i=n-1;i>=1;i--) // 逆推计算f[i][j] (i从n-1到1)
for(j=0;j<=c;j++)
if (j>=w[i] && f[i+1][j]<f[i+1][j-w[i]]+p[i])
f[i][j]= f[i+1][j-w[i]]+p[i];
else
f[i][j]=f[i+1][j];
int cw=c;
printf("背包所装物品如下:\n");
printf(" i w(i) p(i) \n");
printf("----------------------\n");
int sp=0,sw=0;
for (i=1;i<=n-1;i++) // 以表格形式输出结果
if (f[i][cw]>f[i+1][cw])
{
cw-=w[i]; sw+=w[i]; sp+=p[i];
printf("%3d %8d %8d\n",i,w[i],p[i]);
}
if (f[1][c]-sp==p[n])
{
sw+=w[n];sp+=p[n];
printf("%3d %8d %8d\n",n,w[n],p[n]);
}
printf("装载物品重量为 %d ,最大总价值为 %d\n",sw,sp);
return 0;
}
编译并执行以上程序,可得到如下所示的结果。
请输入 n 值:6
请输入背包容量:60
请依次输入每种物品的重量:15 17 20 12 9 14
请依次输入每种物品的价值:32 37 46 26 21 30
背包所装物品如下:
i w(i) p(i)
----------------------
2 17 37
3 20 46
5 9 21
6 14 30
装载物品重量为 60 , 最大总价值为 134
思路1中采用逆推的方法来求解的。实际上在应用动态规划时,还可以顺推求解。
(1)建立递推关系
设f[i][j]为背包容量j,可取物品范围为1、2、…、i的最大效益值。
当0≤j<w[i] 时,物品i不可能装入。最大效益值与f[i−1][j] 相同。
当j≥w[i] 时,有两种选择:
1)不装入物品i,这时最大效益值为f[i−1][j] ;
2)装入物品i,这时已产生效益p[i],背包剩余容量j−w[i],可以选择物品1、2、…、i−1来装,最大效益值为f[i−1][j−w[i]]+p[i] 。
期望的最大效益值是两者中的最大者。于是有递推关系

边界条件为: f[1][ j]= p[1] 当 j≥w[1] 时;
f[1][ j] = 0 当j<w[1] 时。
所求最大效益即最优值为f[n][c]。
(2)顺推计算最优值
for(j=0;j<=c;j++) // 首先计算边界条件f[1][j]
if (j>=w[1] ) f[1][j]=p[1];
else f[1][j]=0;
for (i=2;i<=n;i++) // 顺推计算f[i][j] (i从2到n)
for (j=0;j<=c;j++)
if(j>=w[i] && f[i-1][j]<f[i-1][j-w[i]]+p[i])
f[i][j]= f[i-1][j-w[i]]+p[i];
else f[i][j]=f[i-1][j];
printf("最优值为%d\n",f[n][c]);
(3)构造最优解
若f[i][cw] > f[i-1][cw] ( i=1、2、…、n−1, cw的初始值为c)
则x[i]=1; 装载w[i], cw=cw−x[i]*w[i]。
否则,x[i]=0,不装载w[i]。
最后,所装载的物品效益之和与最优值比较,决定w[1]是否装载。
#include <stdio.h>
#define MAXN 500
#define MAXC 50000
int f[MAXN][MAXC];
int main()
{
int p[MAXN],w[MAXN];
int n,c;
printf("请输入物品的个数 N:");
scanf("%d",&n);
printf("请输入背包容量 C:");
scanf("%d",&c);
printf("请依次输入每种物品的重量:");
int i,j;
for (i=1;i<=n;i++)
scanf("%d",&w[i]);
printf("请依次输入每种物品的价值:");
for (i=1;i<=n;i++)
scanf("%d",&p[i]);
for(j=0;j<=c;j++) // 首先计算边界条件f[1][j]
if(j>=w[1] ) f[1][j]=p[1];
else f[1][j]=0;
for(i=2;i<=n;i++) // 顺推计算f[i][j] (i从2到n)
for(j=0;j<=c;j++)
if(j>=w[i] && f[i-1][j]<f[i-1][j-w[i]]+p[i])
f[i][j]= f[i-1][j-w[i]]+p[i];
else f[i][j]=f[i-1][j];
int cw=c;
printf("背包所装物品如下:\n");
printf(" i w(i) p(i) \n");
printf("----------------------\n");
int sp=0,sw=0;
for (i=n;i>=2;i--) // 以表格形式输出结果
if(f[i][cw]>f[i-1][cw])
{
cw-=w[i]; sw+=w[i]; sp+=p[i];
printf("%3d %8d %8d\n",i,w[i],p[i]);
}
if(f[n][c]-sp==p[1])
{
sw+=w[1];sp+=p[1];
printf("%3d %8d %8d\n",1,w[1],p[1]);
}
printf("装载物品重量为 %d ,最大总价值为 %d\n",sw,sp);
return 0;
}
编译并执行以上程序,得到如下所示的结果。
请输入 n 值:6
请输入背包容量:60
请依次输入每种物品的重量:15 17 20 12 9 14
请依次输入每种物品的价值:32 37 46 26 21 30
背包所装物品如下:
i w(i) p(i)
----------------------
6 14 30
5 9 21
3 20 46
2 17 37
装载物品重量为 60 , 最大总价值为 134
仔细分析编程思路2及其源程序可发现,第 i 件物品的选取决策只与第i-1件有关,与其他无关,即f[i][j]只与f[i-1][j]有关,f[i-2][*]、f[i-3][*]、…这些存储空间的数据是不会再使用的,空间就浪费了。如果采用一维数组,新的状态直接覆盖在旧的上面,迭代使用,就可把空间复杂度从O(N*C)优化为O(C)。
(1)建立递推关系
设f[j]为背包装载的物品容量不超过j时,可获得的最大效益值。
当0≤j<w[i] 时,物品i不可能装入。f[j]的值不改变,无需处理。
当j≥w[i] 时,有两种选择:
1)不装入物品i,这时最大效益值为f[j] ;
2)装入物品i,这时会产生效益p[i],这实际上是在背包容量为j−w[i]的背包中装入物品i,最大效益值为f[j−w[i]]+p[i] 。
期望的最大效益值是两者中的最大者。于是有递推关系
f[j]=max(f[j],f[j-w[i]]+p[i])
所求最大效益即最优值为f[c]。
(2)逆推计算最优值。
在前面使用二维数组时,为了计算最优值,采用顺推和逆推的方法都可以,因为使用二维数组时,中间的所有状态都保留了下来。
但是,使用一维数组时,究竟是使用顺推还是逆推,就需要看具体的问题了。
由于本题中每个物品要么不装入,要么只能装入1次(每个物品只有1件)。因此,只能采用逆推的方法计算最优值。写成如下的循环。
for (i=1;i<=n;i++) // 对每个物品进行处理
for(j=c;j>=w[i];j--) // 逆推计算f[j]
f[j]=max(f[j], f[j-w[i]]+p[i]);
为什么要逆序枚举计算呢?
如果是正序枚举的话,循环写成
for (i=1;i<=n;i++) // 对每个物品进行处理
for(j=w[i];j<=c;j++) // 正序(顺序)计算f[j]
f[j]=max(f[j], f[j-w[i]]+p[i]);
下面我们用简单的测试数据作为示例进行详细说明。
设背包容量C=8,有两件物品,重量分别为w1=2,w2=3;价值分别为p1=3,p2=4。
初始时,f[0]~f[8]全部为0,背包没有装入任何物品,其装入价值显然为0。
采用正序枚举时,当i=1,处理第1件物品,依次的计算过程如下:
f[2]=max { f[2], f[2-w1]+p1 } =max { 0, 0+3} =3
f[3]=max { f[3], f[3-w1]+p1 } =max { 0, 0+3} =3
f[4]=max { f[4], f[4-w1]+p1 } =max { 0, f[2]+3} = 6 (这里实际就出问题了,因为第1件物品只有1件,在计算f[2]时装入了1次,这里又装入1次,不可能的)
f[5]=max { f[5], f[5-w1]+p1 } =max { 0, f[3]+3} =6 (同上,第1件物品又装入了1次)
f[6]=max { f[6], f[6-w1]+p1 } =max { 0, f[4]+3} =9 (第1件物品装入了3次)
f[7]=max { f[7], f[7-w1]+p1 } =max { 0, f[5]+3} =9 (同上,第1件物品装入了3次)
f[8]=max { f[8], f[8-w1]+p1 } =max { 0, f[6]+3} =12 (第1件物品装入了4次)
当i=2,处理第2件物品,依次的计算过程如下:
f[3]=max { f[3], f[3-w2]+p2 } =max { 3, f[0]+4} =4 (第2件物品装入了1次)
f[4]=max { f[4], f[4-w2]+p2 } =max { 6, f[1]+4} =6 (实际是第1件物品装入2次)
f[5]=max { f[5], f[5-w2]+p2 } =max { 6, f[2]+4} =7 (第1件物品装入1次,第2件物品装入1次)
f[6]=max { f[6], f[6-w2]+p2 } =max { 9, f[3]+4} =9 (实际是第1件物品装入3次)
f[7]=max { f[7], f[7-w2]+p2 } =max { 9, f[4]+4} =10 (实际是第1件物品装入2次,第2件物品装入1次)
f[8]=max { f[8], f[8-w2]+p2 } =max { 12, f[5]+4} =12 (实际是第1件物品装入4次)
循环处理结束后,最优值f[8]=12,这显然是不对的,因为只有2件物品,全部装入背包,最大价值也只有3+4=7。
如果采用逆序枚举,我们再来分析循环的处理过程。
当i=1,处理第1件物品,依次的计算过程如下:
f[8]=max { f[8], f[8-w1]+p1 } =max { 0, f[6]+3} =3 (物品1装入背包,背包容量为8)
f[7]=max { f[7], f[7-w1]+p1 } =max { 0, f[5]+3} =3 (物品1装入背包,背包容量为7)
f[6]=max { f[6], f[6-w1]+p1 } =max { 0, f[4]+3} =3 (物品1装入背包,背包容量为6)
f[5]=max { f[5], f[5-w1]+p1 } =max { 0, f[3]+3} =3 (物品1装入背包,背包容量为5)
f[4]=max { f[4], f[4-w1]+p1 } =max { 0, f[2]+3} =3 (物品1装入背包,背包容量为4)
f[3]=max { f[3], f[3-w1]+p1 } =max { 0, f[1]+3} =3 (物品1装入背包,背包容量为3)
f[2]=max { f[2], f[2-w1]+p1 } =max { 0, f[0]+3} =3 (物品1装入背包,背包容量为2)
也就是,物品1的重量为2,可装入背包容量为2~8的背包中,得到最大价值为3。
当i=2,处理第2件物品,依次的计算过程如下:
f[8]=max { f[8], f[8-w2]+p2 } =max { 3, f[5]+4} =7 (实际是物品1和物品2装入背包)
f[7]=max { f[7], f[7-w2]+p2 } =max { 3, f[4]+4} =7 (实际是物品1和物品2装入背包)
f[6]=max { f[6], f[6-w2]+p2 } =max { 3, f[3]+4} =7 (实际是物品1和物品2装入背包)
f[5]=max { f[5], f[5-w2]+p2 } =max { 3, f[2]+4} =7 (实际是物品1和物品2装入背包)
f[4]=max { f[4], f[4-w2]+p2 } =max { 3, f[1]+4} =4 (实际是物品2装入背包)
f[3]=max { f[3], f[3-w2]+p2 } =max { 3, f[0]+4} =4 (实际是物品2装入背包)
由上面的计算过程知,逆推计算时,每个物品若装入背包,最多装入1次。
由上面的分析大家也可产生一个印象,若每种物品只有1件,1个物品装入背包最多只能装入1次,则采用逆序递推的方法计算最优值,这也是0/1背包的基本模式;若每种物品有无数件,可以不限次数地装入背包中,则采用顺序(正序)递推的方法计算最优值,这也是完全背包的基本模式。对于0/1背包和完全背包,后面会进行更详细地阐述。
(3)构造最优解。
如果要求输出某个最优解,需要记录每个状态的最优值是由状态转移方程的哪一项推出来的。
如果我们知道了当前状态是由哪一个状态推出来的,就能容易的输出某个最优解了。
为此,最简单的方法是定义数组g[n][C],其中g[i][j]就记录第i件物品在加入背包时,其状态f[j]是由状态转移方程f[j]=max(f[j], f[j-w[i]]+p[i])哪一项推出。若第i件物品加入了背包,即f[j]= f[j-w[i]]+p[i],置g[i][j]=1;若第i件物品不加入背包,即f[j]=f[j],置g[i][j]=0。
改写上面的逆推计算最优值循环如下。
for (i=1;i<=n;i++) // 对每个物品进行处理
for(j=c;j>=w[i];j--) // 逆推计算f[j]
{
if (f[j]<f[j-w[i]]+p[i])
{
f[j]=f[j-w[i]]+p[i];
g[i][j]=1; // 选择第i件物品装入
}
else
g[i][j]=0; // 不选择第i件物品装入
}
由此,可用如下循环输出某个最优解。
int T=c;
for (i=n;i>=1;i--)
{
if (g[i][T])
{
printf("used %d",i);
T-=w[i]; //减去物品i的重量
}
}
当然,为了输出某个最优解,我们又定义了一个二维数组,这样我们采用一维数组进行优化的目的并没有达到,还不如直接像编程思路1或编程思路2那样,直接采用二维数组保存各状态,再构造出最优解。
但是,如果只要求得到最优值,而无需输出某个最优解,采用一维数组解决问题还是非常有意义的。
#include <stdio.h>
#define MAXN 500
#define MAXC 50000
int f[MAXC]={0};
int g[MAXN][MAXC];
int main()
{
int n,c;
printf("请输入物品的个数 N:");
scanf("%d",&n);
printf("请输入背包容量 C:");
scanf("%d",&c);
printf("请依次输入每种物品的重量:");
int p[MAXN],w[MAXN];
int i,j;
for (i=1;i<=n;i++)
scanf("%d",&w[i]);
printf("请依次输入每种物品的价值:");
for (i=1;i<=n;i++)
scanf("%d",&p[i]);
for (i=1;i<=n;i++) // 对每个物品进行处理
for(j=c;j>=w[i];j--) // 逆推计算f[j]
{
if (f[j]<f[j-w[i]]+p[i])
{
f[j]=f[j-w[i]]+p[i];
g[i][j]=1; // 选择第i件物品装入
}
else
g[i][j]=0; // 不选择第i件物品装入
}
printf("背包所装物品如下:\n");
printf(" i w(i) p(i) \n");
printf("----------------------\n");
int t=c;
for (i=n;i>=1;i--)
{
if (g[i][t])
{
printf("%3d %8d %8d\n",i,w[i],p[i]);
t-=w[i]; // 减去物品i的重量
}
}
printf("装载物品重量为 %d ,最大总价值为 %d\n",c-t,f[c]);
return 0;
}
编译并执行以上程序,得到如下所示的结果。
请输入物品的个数 N:6
请输入背包容量 C:60
请依次输入每种物品的重量:15 17 20 12 9 14
请依次输入每种物品的价值:32 37 46 26 21 30
背包所装物品如下:
i w(i) p(i)
----------------------
6 14 30
5 9 21
3 20 46
2 17 37
装载物品重量为 60 ,最大总价值为 134
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po
尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search
由于fast-stemmer的问题,我很难安装我想要的任何rubygem。我把我得到的错误放在下面。Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingfast-stemmer:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcreatingMakefilemake"DESTDIR="cleanmake"DESTDIR=
我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport: