系列如此之远
代码审查
现在, 我们将查看使用 apache spark 机器学习库、spark 版本2.3.1 和 java 版本8实现算法的代码。完整的代码可在github上使用。
帮助器方法和类
下面的方法将由0组成的前缀添加到参数后缀中, 以便前缀 + 后缀的长度等于输入参数大小。如果后缀的长度≥大小, 则不添加前缀。假定大小≥1和后缀不是空的。
private static String pad(int size, String suffix){
String zero = "0";
StringBuilder bldr = new StringBuilder();
for(int i = 0; i < size - suffix.length(); i++){
bldr.append(zero);
}
return bldr.toString() + suffix;
}
下面的方法返回由所有非负整数 & lt; 2 大小的 base-2表示字符串组成的数组。数组中的每个字符串的长度等于输入参数的大小, 如有必要, 用0填充。例如, base2(3)返回以下内容:
{1000, 0010, 011, 100, 110, 110, 110, 111}
假定大小≥1。
private static String[] base2(int size){
int dimension = (int)Math.pow(2,size);
String[] retArr = new String[dimension];
for(int i = 0; i < dimension; i++){
String tmp = Integer.toString(i,2);
retArr[i] = pad(size,tmp);
}
return retArr;
}
下面的方法返回两个向量的加法 (如果 < cn/> 为真) 或减法 (如果 < c2/> 是假的)apache.spark.ml.linalg 包。
/**
@param a: Vector of doubles
@param b: Vector of doubles
@param isAdd: true to add, false to subtract
*/
private static DenseVector addOrSubtract(Vector a, Vector b, boolean isAdd){
double[] tmp1 = a.toArray();
double[] tmp2 = b.toArray();
double[] tmp3 = new double[tmp1.length];
int factor = 1;
if(!isAdd){
factor = -1;
}
for(int i = 0; i < tmp1.length; i++){
tmp3[i] = tmp1[i] + factor*tmp2[i];
}
return new DenseVector(tmp3);
}
下面的两个类主要用于 c (p) 的计算。
class AssignmentsAndUtility {
double[] assignments;
double utility;
}
class AssignmentsAndPrice{
ArrayList<double[]> assignmentsL;
Vector price;
}
对于代理 i, 下面的方法 < c//> 以价格 p计算 v i (p), 即它解决了最大化问题:
最大x x x i {ui (x)-p * x}
其中 xi是代理的消耗集。
回想一下
-
ui = [ui1 ui1. ..
-
ui(x) = ui t * x = 1, 2,…, k (u ij * x ij)
-
ui(x)-p * x = j = 1, 2,… , k (u ij-p j) * xij
此外, 对于 xi中的向量, 维度 j 对应于特定的资源类型, 并且小于或等于 lij。因此, 该方法计算最大化器, 以便:
-
xij = lij, 如果您是 ij-p j & gt; 0
-
xij = 0, 如果您是 ij-p j≤0
请注意, 最大化 x 不一定是唯一的, 因为如果您的 ij-p j≤0 , 那么我们可以选择任何 xij 满足 0≤xij≤l ij .
输入参数 < cn/> 是一个向量, 其中 j-th 元素等于 lij (i 对应于特定的代理)。该方法从各种变量定义开始。
/**
@param U: K-dimensional vector of value coefficients (K := # resource types).
@param P: K-dimensional price vector.
@param consumptionSet: K-dimensional vector, upper bounds on each
resource type.
*/
private static AssignmentsAndUtility getMaxUtility(Vector U, Vector P,
Vector consumptionSet){
double[] tmp1 = addOrSubtract(U,P,false).toArray();
double[] tmp2 = new double[tmp1.length];
double[] normalized = new double[tmp1.length];
...
数组 < cn/> 具有 k 元素, 每个条目存储 uij-p j, j = 1,…, k。如果 u ij-p j & lt ; 0 和您 ij-p j 否则, j = 1,….., k, 则 < c\ > 中的条目将为 0.如果 uij-p j & lt ; 0 和1否则, < cn/中的条目将为 0, j = 1,..., k。
int j = 0; // j is an index to resource types
for(double dbl:tmp1){
if(dbl < 0d){
tmp2[j] = 0d;
normalized[j] = 0d;
j++;
}else{
tmp2[j] = dbl;
normalized[j] = 1d;
j++;
}
}
.
然后, 我们从 < c2/> 构造 org.apache.spark.ml.linalg.DenseMatrix 对象 < cn/>, 并利用它来计算最大值 x x i {u i (x)-p * x} (< c/ > 对象)。最后, < cn/> 用于存储回报 < c2/和相应的 < cn/>。
DenseMatrix uMinusp = new DenseMatrix(1, tmp2.length, tmp2);
double result = (uMinusp.multiply(consumptionSet).toArray())[0];
AssignmentsAndUtility returnObject = new AssignmentsAndUtility();
returnObject.utility = result;
returnObject.assignments = normalized;
return returnObject;
}
该方法 < cn/将从 < c2/中反复调用, 接下来将讨论。
主要方法
计算最小 ω (p + ω)
下面的方法计算并返回最小克ω (p + ω) 以及相应的分配.
/**
@param Ul: A list of value coefficients for agents. List consists of N
vectors, one for each agent, where each vector is K-dimensional (N :=
number of agents, K := number of resource types).
@param P: K-dimensional price vector.
@param consumptionSets: List of upper bounds on each resource type. List
consists of N vectors, one for each agent, where each vector is
K-dimensional.
@param S: K-dimensional vector where the j-th entry corresponds to available
supply units.
@param Omega: All possible 1-unit price increases over the price vector.
*/
private static AssignmentsAndPrice getCMin(ArrayList<Vector> Ul,
Vector P, ArrayList<Vector> consumptionSets, Vector S, String[] Omega){
...
该方法从变量初始化开始。变量 < cn/> 最终将成为“分配”。变量 < c/t > 对应于 ptmp。
// Variable initializations.
ArrayList<double[]> optimal = null;
double minimum = -1d;
Vector P_tmp = null;
String optimalRepresentation = null;
...
下面, 循环 < cn/> 对应于ω中每个元素的重复语句变量 < c/org 对应于 p。当循环结束时, ptmp (= min ω c (p + ω)) 已被计算, 并且和可变 < c2 > 已成为分配。
for(String s:Omega){
AssignmentsAndUtility tmpObj = null;
double[] tmp = new double[s.length()];
for(int j = 0; j < tmp.length; j++){
tmp[j] = Character.getNumericValue(s.charAt(j));
}
DenseVector omega_v = new DenseVector(tmp);
Vector P_omega = addOrSubtract(P,omega_v,true);
double maximum = ((new DenseMatrix(1, P_omega.size(),
P_omega.toArray())).multiply(S).toArray())[0];
ArrayList<double[]> tmpArr = new ArrayList<double[]>();
for(int i = 0; i < Ul.size(); i++){
tmpObj = getMaxUtility(Ul.get(i), P_omega,
consumptionSets.get(i));
tmpArr.add(tmpObj.assignments);
maximum += tmpObj.utility;
}
if(minimum < 0 || maximum < minimum){
minimum = maximum;
optimal = tmpArr;
optimalRepresentation = s;
P_tmp = P_omega;
}
}
...
我们最终返回由 < c2/ > 对象封装的 ptmp和分配。
AssignmentsAndPrice result = new AssignmentsAndPrice();
result.price = P_tmp;
result.assignmentsL = optimal;
return result;
}
计算第1部分: 最优价格和相应的分配
下面的方法执行第1部分, 利用 < cn/ 计算最佳价格向量。
/**
@param Ul: A list of value coefficients for agents. List consists of N
vectors, one for each agent, where each vector is K-dimensional (N :=
number of agents, K := number of resource types).
@param S: K-dimensional vector where the j-th entry corresponds to available
supply units.
@param consumptionSets: List of upper bounds on each resource type. List
consists of N vectors, one for each agent, where each vector is
K-dimensional.
*/
private static AssignmentsAndPrice getOptimalAssignmentsAndPrice(
ArrayList<Vector> Ul, Vector S, ArrayList<Vector> consumptionSets){
...
该方法从各种初始化开始。特别是, 变量 < cn/> 将封装最佳价格向量 po 和优化分配。变量 < c/> 和 < cqu> 分别对应于 pnew和ω。
Vector P = Vectors.zeros(S.size());
Vector P_new = null;
AssignmentsAndPrice Optimal = null;
String[] Omega = base2(P.size());
...
同时循环将一直运行, 直到价格不再上涨。从上一节中回忆 < c0/> 计算并返回最小 ω c (p + ω) 以及相应的分配价格;
{优化 = 新的分配和价格 ();
最优价格 = p _ new;
最优. 分配 l = 结果. 分配 l;
打破;
} 其他 {p = p _ new;
}
}
…
最后, 该方法返回封装 p o 和优化分配的变量 < cn />。
return Optimal;
}
计算第2部分: 与最优价格相对应的拨款
下面的方法执行第2部分, 即对于每个资源类型, 可用的供应分配给对资源类型的出价高于该资源类型的设置价格的代理 (如果有) 进行优先排序。调用 < cn/> 时, 我们将传递参数 p 的 p和参数 < cn/的优化分配。
/**
@param P: K-dimensional price vector.
@param Ul: A list of value coefficients for agents. List consists of N
vectors, one for each agent, where each vector is K-dimensional (N :=
number of agents, K := number of resource types).
@param S: K-dimensional vector where the j-th entry corresponds to available
supply units.
@param consumptionSets: List of upper bounds on each resource type. List
consists of N vectors, one for each agent, where each vector is
K-dimensional.
@param assignmentsL: List of N assignments, each has size K, where an entry
equals 0 or 1
*/
private static ArrayList<DenseVector> getAllocations(Vector P,
ArrayList<Vector> Ul, Vector S, ArrayList<Vector> consumptionSets,
ArrayList<double[]> assignmentsL){
...
我们从初始化各种临时变量开始。特别是, 变量 < c/> 对应于 allocij,即将资源类型 j 分配给代理 i。
int K = S.size(); // size of supply vector
int N = assignmentsL.size(); // size of bidders
double P_vals[] = P.toArray();
ArrayList<double[]> Ul_vals = new ArrayList<double[]>();
for(Vector v:Ul){
Ul_vals.add(v.toArray().clone());
}
ArrayList<double[]> consumptionSet_vals = new ArrayList<double[]>();
for(Vector v:consumptionSets){
consumptionSet_vals.add(v.toArray().clone());
}
double S_vals[] = S.toArray();
ArrayList<double[]> Alloc = new ArrayList<double[]>();
// Initialize allocations
for(int i = 0; i < N; i++){
double[] vals = new double[K];
Alloc.add(vals);
}
...
然后, 我们逐一执行每个资源类型的分配, 从定义初始供应开始, zj: = s j (请参见双 z _ j = s _ val [j])。正如算法中提到的, 分配是在两个不同的循环中完成的;f源泉1优先于值大于相应资源类型的设定价格的代理, 第二个代理不考虑任何优先级。
for(int j = 0; j < K; j++){
double Z_j = S_vals[j];
while(Z_j > 0) {
int bidderIndex = 0;
// if for that i there is any bidder with nonzero allocation and
// ui > price allocate to it
for (double[] bidder : Alloc) {
if (assignmentsL
获取 (投标索引) [j] & gt; 0) {如果 (z _ j & lt; = 0) {中断;
} if (ul _ vals. get (bidderindex) [j]-p _ val [j] & gt; 0) {双 delta = math.min (z _ j, 消费集 _ vals. get. get (投标指数) [j]);
消费集 _ vals. get (bidderindex) [j] = 消费集 _ vals. get (bidderindex) [j]-delta;
z _ j = z _ j-delta;
投标人 [j] = 投标人 [j] + delta;
}} 投标指数 +;
} bidderindex = 0;
为 (双重 [] 投标人: aloc) {如果 (分配 l. get (投标索引) [j] & gt; 0 & amp; c & amp; 消费集 _ vals. get (投标指数) [j] & gt; 0) {如果 (z _ j & lt; = 0) {中断;
} 双 delta = math.min (z _ j, 消耗量 set _ vals. get (投标指数) [j]);
消费集 _ vals. get (bidderindex) [j] = 消费集 _ vals. get (bidderindex) [j]-delta;
z _ j = z _ j-delta;
投标人 [j] = 投标人 [j] + delta;
} 投标指数 +;
}
}
}
…
最后, 该方法构造最终的 < cn/ > 变量并将其返回。
ArrayList<DenseVector> allocations = new ArrayList<DenseVector>();
for(double[] allocation:Alloc){
allocations.add(new DenseVector(allocation));
}
return allocations;
}
这就是第3部分!明天调整, 当我们将把所有这些片段放在一起的代码中结束这个系列。